Strategy Design Pattern: Demonstration
- Biyi Akinpelu
- Apr 13, 2023
- 4 min read
Updated: Apr 14, 2023
Tom wrote a function that accepts two numbers, a and b, as well as the operator o. The result of the corresponding mathematical function on both numbers should be returned by his function. The function should return null if the operator is not one of the specified characters +, -, /, *, or if there is a division by zero. Please assist him by repairing the code.
Code example in JavaScript
function calculate(a, b, o) {
if (o === "+") {
return a + b;
} else if (o === "-") {
return a - b;
} else if (o === "*") {
return a * b;
} else if (o === "/") {
if (b === 0) {
return null;
} else {
return a / b;
}
} else {
return null;
}
}
Problem statement
Identifying a problem with this solution, reveals that there's to many if-else-if statements within the above code example. The current solution is using multiple if-else statements to check the operator, which can be replaced by a switch statement. The N-Path complexity is high.
N-Path Complexity
N-Path complexity is a software metric that measures the number of unique possible execution paths through a section of code, considering all possible combinations of input values. It is based on the premise that as the number of possible execution paths increases, the likelihood of defects also increases.
In simple terms, the N-Path complexity of a code section is the number of possible paths that a set of inputs can take through that section. It is a useful metric for identifying sections of code that are likely to contain errors or be difficult to maintain.
For the above solution, the N-Path complexity is 8.
Proposed solution 1
Here's an optimized solution using a switch statement:
function calculate(a, b, o) {
let result;
switch (o) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
if (b === 0) {
result = null;
} else {
result = a / b;
}
break;
default:
result = null;
}
return result;
}
This solution uses a switch statement to check the operator and perform the corresponding mathematical operation. It also includes a check for division by zero, and returns null if this occurs or if an invalid operator is provided. The N-Path and cyclomatic complexity is still high but has been reduced to 6. i.e. six (6) possible paths of execution.
Problem with above solution
The above solution even though it's slightly comprehensible, still falls under the category of code that smells, because it has several paths of code execution. The N-Path complexity is high.
Proposed solution 2
Firstly, the above solution is a good example of a spaghetti code which is sometimes referred to as code smells, meaning, it's not easily comprehensible, especially if the code is reused everywhere in your project. Hence, we need take a better approach to refactor the code by simplifying the code using a switch statement in Java.
public static Double calculator(double a, double b, char operator) {
switch(operator) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0) {
return null;
}
return a / b;
default:
return null;
}
}
This code will take in two doubles a and b, as well as a character operator. The switch statement will evaluate the operator and perform the appropriate mathematical operation on a and b, returning the result as a Double. If the operator is not one of the specified characters or if there is a division by zero, null will be returned.
As you can see, the solution still has a high N-Path complexity making readability and maintenance of code slightly difficult. Over time, spaghetti codes can render the system problem prone, unreusable and unmaintainable.
Therefore, we will progressively address the above issue using the Strategy design pattern.
Strategy Design Pattern

One of the better ways to optimize the calculator code using design patterns is to use the Strategy pattern. Here's how we can implement it in Java:
First, we define an interface Operation that defines a method perform to perform a mathematical operation:
interface Operation {
double perform(double num1, double num2);
}
Next, we create concrete classes that implement the Operation interface for each mathematical operation, like Addition, Subtraction, Multiplication, and Division:
class Addition implements Operation {
public double perform(double num1, double num2) {
return num1 + num2;
}
}
class Subtraction implements Operation {
public double perform(double num1, double num2) {
return num1 - num2;
}
}
class Multiplication implements Operation {
public double perform(double num1, double num2) {
return num1 * num2;
}
}
class Division implements Operation {
public double perform(double num1, double num2) {
if (num2 == 0) {
throw new ArithmeticException("Division by zero");
}
return num1 / num2;
}
}
Then, we create a Calculator class that takes two numbers and an operator, and performs the corresponding operation using the Operation interface:
class Calculator {
private Operation operation;
public void setOperation(Operation operation) {
this.operation = operation;
}
public double calculate(double num1, double num2) {
return operation.perform(num1, num2);
}
}
Finally, we use the Calculator class with the appropriate Operation object to perform the desired calculation:
Calculator calculator = new Calculator();
// Addition
calculator.setOperation(new Addition());
double result = calculator.calculate(10, 5);
System.out.println(result); // Output: 15.0
// Subtraction
calculator.setOperation(new Subtraction());
result = calculator.calculate(10, 5);
System.out.println(result); // Output: 5.0
// Multiplication
calculator.setOperation(new Multiplication());
result = calculator.calculate(10, 5);
System.out.println(result); // Output: 50.0
// Division
calculator.setOperation(new Division());
result = calculator.calculate(10, 5);
System.out.println(result); // Output: 2.0
// Division by zero
calculator.setOperation(new Division());
result = calculator.calculate(10, 0); // Throws ArithmeticException
This way, we can add new operations to our calculator without modifying the existing code, and we can easily switch between operations by setting the appropriate Operation object.
As you can see, the benefits of using design patterns in general outweighs any alternative approach. My advice for developers in general is to consider the situation and kind of development or software design problem you are dealing with, sometimes, it may not be necessary to use design patterns, but it is important to note that you are more than likely to introduce more defects if you do not use it.
コメント