ARTICLE

Good and bad flexibility in code

By Christian Clausen

Definition: Flexibility

Flexibility is an expression of how easily a codebase accepts desired changes. If developers have a good sense of what changes are coming in the future, they can prepare for these changes. This makes changes easy to implement and makes the codebase flexible. Some changes may fall outside of what developers have prepared for, and may require major adjustments or even rewrites of big parts of the codebase. Flexibility, therefore, can be thought of as a ratio of easy changes to difficult ones.

Before```Typescript
for (let i = 0; i < as.length; i++)
as[i] += 2;
```
After
```Typescript
as = as.map(x => x + 2);
```

Pros and cons of flexibility

With experience comes nuances, and throughout my career I have learned that nothing is universally good or bad. Everything has its use case, although some are more common than others. This is also the case for flexibility. Depending on the circumstances, it can be good or bad. Let’s discuss some of the important pros and cons.

Lifting flexibility out of code

As I hinted in the beginning, the right balance lies in the structure of the code. Now, I’ll show how to take advantage of this fact, and expose the appropriate flexibility for this particular code through small safe steps. Imagine we have two functions, then by unifying them we will get the flexibility that we want. The two functions find the maximum element and the sum of an array, respectively.

```Typescript
function maxArr(arr: number[]) {
let maxSoFar = -Infinity;
for (let i = 0; i < arr.length; i++) {
if (maxSoFar < arr[i])
maxSoFar = arr[i];
}
return maxSoFar;
}
function sumArr(arr: number[]) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
```
Before```Typescript

function maxArr(arr: number[]) {
let maxSoFar = -Infinity;
for (let i = 0; i < arr.length; i++) {
if (maxSoFar < arr[i])
maxSoFar = arr[i];
}
return maxSoFar;
}
function sumArr(arr: number[]) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}

```
After```Typescript
class Arr {
max(arr: number[]) {
let maxSoFar = -Infinity;
for (let i = 0; i < arr.length; i++) {
if (maxSoFar < arr[i])
maxSoFar = arr[i];
}
return maxSoFar;
}
sum(arr: number[]) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
}
```
Before```Typescript
class Arr {




max(arr: number[]) {
let maxSoFar = -Infinity;
for (let i = 0; i < arr.length; i++) {
if (maxSoFar < arr[i])
maxSoFar = arr[i];
}
return maxSoFar;
}
sum(arr: number[]) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
}
```
After```Typescript
class Arr {
private arr: number[];
constructor(arr: number[]) {
this.arr = arr;
}
max() {
let maxSoFar = -Infinity;
for (let i = 0; i < this.arr.length; i++) {
if (maxSoFar < this.arr[i])
maxSoFar = this.arr[i];
}
return maxSoFar;
}
sum() {
let total = 0;
for (let i = 0; i < this.arr.length; i++) {
total += this.arr[i];
}
return total;
}
}
```
Before```Typescript
class Arr {
// ...
max() {
let maxSoFar = -Infinity;
for (let i = 0; i < this.arr.length; i++) {
if (maxSoFar < this.arr[i])
maxSoFar = this.arr[i];
}
return maxSoFar;
}
sum() {
let total = 0;
for (let i = 0; i < this.arr.length; i++) {
total += this.arr[i];
}
return total;
}
}
```
After```Typescript
class Arr {
// ...
max() {
let result = -Infinity;
for (let i = 0; i < this.arr.length; i++) {

result = Math.max(result, this.arr[i]);
}
return result;
}
sum() {
let result = 0;
for (let i = 0; i < this.arr.length; i++) {
result = result + this.arr[i];
}
return result;
}
}
```
Max```Typescript
max() {
let result = -Infinity;
for (let i = 0; i < this.arr.length; i++) {
result = Math.max(result, this.arr[i]);
}
return result;
}
```
Sum```Typescript
sum() {
let result = 0;
for (let i = 0; i < this.arr.length; i++) {
result = result + this.arr[i];
}
return result;
}
```
Common structure```Typescript
___() {
let result = ____;
for (let i = 0; i < this.arr.length; i++) {
result = ______________;
}
return result;
}
```
Before```Typescript
___() {
let result = ____;
for (let i = 0; i < this.arr.length; i++) {
result = ______________;
}
return result;
}
```
After```Typescript
___(start: number) {
let result = start;
for (let i = 0; i < this.arr.length; i++) {
result = ______________;
}
return result;
}
```
Before```Typescript




class Arr {
// ...
___(start: number)

{
let result = start;
for (let i = 0; i < this.arr.length; i++) {
result = ______________;
}
return result;
}
}
```
After```Typescript
interface Operator {
calc(result: number, elem: number): number;
}

class Arr {
// ...
___(start: number,
op: Operator)
{
let result = start;
for (let i = 0; i < this.arr.length; i++) {
result = op.calc(result, this.arr[i]);
}
return result;
}
}
```
Before```Typescript
class Arr {
// ...
max() {
let result = -Infinity;
for (let i = 0; i < this.arr.length; i++) {
result = Math.max(result, this.arr[i]);
}
return result;
}
}
```
After```Typescript
class MaxOperator implements Operator {
calc(result: number, elem: number) {
return Math.max(result, elem);
}
}
class Arr {
// ...
max() {
return this.reduce(-Infinity,
new MaxOperator());
}
}
```
Before```Typescript
class Arr {
// ...
sum() {
let result = 0;
for (let i = 0; i < this.arr.length; i++) {
result = result + this.arr[i];
}
return result;
}
}
```
After```Typescript
class SumOperator implements Operator {
calc(result: number, elem: number) {
return result + elem;
}
}
class Arr {
// ...
sum() {
return this.reduce(0,
new SumOperator());
}
}
```
Before```Typescript
class Arr {
// ...
reduce(start: number,
op: Operator)
{
let result = start;
for (let i = 0; i < this.arr.length; i++) {
result = op.calc(result, this.arr[i]);
}
return result;
}
max() {
return this.reduce(-Infinity,
new MaxOperator());
}
sum() {
return this.reduce(0,
new SumOperator());
}
}
```
After```Typescript
class Arr {
// ...
reduce(start: number,
op: (a: number, b: number) => number)
{
let result = start;
for (let i = 0; i < arr.length; i++) {
result = op(result, arr[i]);
}
return result;
}
max() {
return this.reduce(-Infinity,
(a, b) => Math.max(a, b));
}
sum() {
return this.reduce(0,
(a, b) => a + b);
}
}
```

Conclusion

I hope you have enjoyed this unusual presentation of Introduce Strategy Pattern, and a few other refactoring patterns. You will find deeper explorations of these and many more practices to improve your code in my book Five Lines of Code. You can also find rules for when and how to apply them, making refactoring even the most complicated codebases a breeze.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Manning Publications

Follow Manning Publications on Medium for free content and exclusive discounts.