This is an interesting refatoring that I saw applied for the first time recently in the project that I’m currently working on. I think this refactoring is quietly simple and extremely useful in many kind of situations.
A Conditional Dispatcher is a pattern which consists in executing actions based on a conditional statement (like if/elses or switch/cases). When well used, conditional dispatchers handle a small number of requests without any complex logic. In those cases, the Command pattern will not produce a better design and can even complicate the program’s logic.
On the other hand, when handlers are too big and reading the code becomes fastidious, refactoring to commands can be a good option. This pattern consists in creating an abstract class of interface that defines an execution method (like run(), execute(), etc.). By extending or implementing the class or interface, respectively, we have the possibility to add behavior dynamically in a flexible way.
In general, there are two good reasons to replace conditional dispatchers with command:
- When conditional dispatchers become so big that it becomes difficult to understand the underlying logic;
- When all the handling logic is hard-coded and it lacks of flexibility to configure new actions and logic at runtime (in other words, polymorphically dead).
This refactoring has the following advantages and drawbacks:
- (+) Simple implementation;
- (+) Offers the possibility to execute different behavior in a uniform way;
- (+) Add flexibility to the design in terms of polymorphism;
- (-) Can lead to a complex design when conditional dispatcher is enough.
Mechanics
To replace Dispatcher Conditional by Command, follow the steps bellow.
- Find code containing the conditional dispatcher. Apply Extract Method on each behavior in such a way every handled action calls its own method.
- Apply Extract Class to isolate each execution method created on step one inside his own class. This step will create many concrete commands. Use Compose Method if the new method is too large or Form Template Method to avoid code duplication.
- Create an abstract class or interface and define an execution method on it.
- Next, modify every concrete command to make them implement interface or extend the abstract class created. It may be necessary to modify some methods in order to achieve a single method signature.
- Update the callers to use the abstract class or interface create in lieu of a concrete command.
- Modify the class containing the conditional dispatcher and create a Map containing all the concrete commands and associate a key to each of them. Make the Map an instance variable.
- Replace the conditional statement by searching into the Map for the right command. Use the abstract class or interface to hold the command instance. Finally, handle the action.
Example
Let’s consider the following Java class containing a single Conditional Dispatcher for calculating salaries.
public class SalaryCalculator {
public float calculate(Employee employee) {
float salary = 0;
switch(employee.getFunction()) {
case SELLER:
salary = calculateComission(employee) + getBaseSalary();
break;
case MANAGER:
salary = getBaseSalary() * getWeightByFunction(MANAGER);
break;
case ENGINEER:
PersonalGoals goalsReached = getPersonnalObjectifs(employee);
salary = getBaseSalary() * getWeightByFunction(ENGINEER) + calculateVariableRemunaration(goalsReached);
break;
case CONSULTANT:
salary = getBaseSalary() * getWeightByFunction(CONSULTANT) + calculateBenefitsByFunction(CONSULTANT);
break;
case OWNER:
salary = whatsLeftAfterPayingEveryone();
break;
// And many other cases here
}
return salary;
}
// More and more code here...
}
In the step one we are going to extract the logic inside each case into its own method applying Extract Method. By doing this, we simplify the code in such a way each action will be handled by one method.
public class SalaryCalculator {
public float calculate(Employee employee) {
float salary = 0;
switch(employee.getFunction()) {
case SELLER:
salary = calculateSellerSalary(employee);
break;
case MANAGER:
salary = calculateManagerSalary();
break;
case ENGINEER:
salary = calculateEngineerSalary(employee);
break;
case CONSULTANT:
salary = calculateConsultantSalary();
break;
case OWNER:
salary = calculateOwnerSalary();
break;
// And many other cases here
}
return salary;
}
private float calculateOwnerSalary() {
return whatsLeftAfterPayingEveryone();
}
private float calculateConsultantSalary() {
return getBaseSalary() * getWeightByFunction(CONSULTANT) + calculateBenefitsByFunction(CONSULTANT);
}
private float calculateEngineerSalary(Employee employee) {
PersonalGoals goalsReached = getPersonnalObjectifs(employee);
return getBaseSalary() * getWeightByFunction(ENGINEER) + calculateVariableRemunaration(goalsReached);
}
private float calculateManagerSalary() {
return getBaseSalary() * getWeightByFunction(MANAGER);
}
private float calculateSellerSalary(Employee employee) {
return calculateComission(employee) + getBaseSalary();
}
// More and more code here...
}
The step two consists of applying Extract Class for each method created on step one.
public class SalaryCalculator {
private final OwnerSalaryCalculator ownerSalaryCalculator = new OwnerSalaryCalculator();
private final ConsultantSalaryCalculator consultantSalaryCalculator = new ConsultantSalaryCalculator();
private final EngineerSalaryCalculator engineerSalaryCalculator = new EngineerSalaryCalculator();
private final ManagerSalaryCalculator managerSalaryCalculator = new ManagerSalaryCalculator();
private final SellerSalaryCalculator sellerSalaryCalculator = new SellerSalaryCalculator();
public float calculate(Employee employee) {
float salary = 0;
switch(employee.getFunction()) {
case SELLER:
salary = sellerSalaryCalculator.calculateSellerSalary(employee);
break;
case MANAGER:
salary = managerSalaryCalculator.calculateManagerSalary();
break;
case ENGINEER:
salary = engineerSalaryCalculator.calculateEngineerSalary(employee);
break;
case CONSULTANT:
salary = consultantSalaryCalculator.calculateConsultantSalary();
break;
case OWNER:
salary = ownerSalaryCalculator.calculateOwnerSalary();
break;
// And many other cases here
}
return salary;
}
}
This step produces as many concrete commands as actions handled by the switch, like ConsultantSalaryCalculator, EngineerSalaryCalculator, etc.
public class ConsultantSalaryCalculator extends AbstractSalaryCalculator {
float calculateConsultantSalary() {
float salary;
salary = getBaseSalary() * getWeightByFunction(CONSULTANT) + calculateBenefitsByFunction(CONSULTANT);
return salary;
}
}
As many methods in the class SalaryCalculator were called by the new concrete commands, I decided to apply From Template Method to regroup them inside an abstract class. By doing so, I avoid code duplication.
public abstract class AbstractSalaryCalculator {
protected float whatsLeftAfterPayingEveryone() {...}
protected float calculateBenefitsByFunction(FUNCTION function) {...}
protected float calculateVariableRemunaration(PersonalGoals objectifs) {...}
protected PersonalGoals getPersonnalObjectifs(Employee employee) {...}
protected float getWeightByFunction(FUNCTION function) {...}
protected float calculateComission(Employee e) {...}
protected float getBaseSalary() {...}
}
On the third step we will create an interface that declares an execution method. As we already created an abstract class, we could declare an abstract method on it. By doing this, every class extending from the abstract class needs to implement the execution method. For this refactoring, I preferred to create an interface to not mix two refactorings.
public interface Calculator {
float calculate(Employee employee);
}
In the step four, we are going to modify each concrete command in order to implement the interface create on step three. The original method names are so going to chance, since now we will use the calculate method. But, as every concrete class already extends from AbstractSalaryCalculator, I’m going to implement the Calculator interface on that class. This avoid me to implement the interface on each concrete command.
public abstract class AbstractSalaryCalculator implements Calculator {...}
Now, each concrete command must implement the calculate method.
public class OwnerSalarCalculator extends AbstractSalaryCalculator {
@Override
public float calculate(Employee employee) {
return whatsLeftAfterPayingEveryone();
}
}
On the step five, we are going to update the client to use the interface instead of concrete commands. This step will allows us to call the write command polymorphically.
public class SalaryCalculator {
public float calculate(Employee employee) {
float salary = 0;
switch(employee.getFunction()) {
case SELLER:
Calculator sellerSalaryCalculator = new SellerSalaryCalculator();
salary = sellerSalaryCalculator.calculate(employee);
break;
case MANAGER:
Calculator managerSalaryCalculator = new ManagerSalaryCalculator();
salary = managerSalaryCalculator.calculate(employee);
break;
case ENGINEER:
Calculator engineerSalaryCalculator = new EngineerSalaryCalculator();
salary = engineerSalaryCalculator.calculate(employee);
break;
case CONSULTANT:
Calculator consultantSalaryCalculator = new ConsultantSalaryCalculator();
salary = consultantSalaryCalculator.calculate(employee);
break;
case OWNER:
Calculator ownerSalaryCalculator = new OwnerSalaryCalculator();
salary = ownerSalaryCalculator.calculate(employee);
break;
// And many other cases here
}
return salary;
}
}
On step six, we will create a Map to hold instances of every concrete command associated with a key.
public class SalaryCalculator {
private Map<FUNCTION, Calculator> handlers;
public SalaryCalculator() {
handlers = new HashMap<FUNCTION, Calculator>();
handlers.put(FUNCTION.SELLER, new SellerSalaryCalculator());
handlers.put(FUNCTION.MANAGER, new ManagerSalaryCalculator());
handlers.put(FUNCTION.ENGINEER, new EngineerSalaryCalculator());
handlers.put(FUNCTION.CONSULTANT, new ConsultantSalaryCalculator());
handlers.put(FUNCTION.OWNER, new OwnerSalaryCalculator());
}
public float calculate(Employee employee) {
float salary = 0;
switch(employee.getFunction()) {...}
}
Finally, on step seven, we will replace the switch statement by a search into the Map. Now, since we have the key, we are able to handle concrete commands polymorphically.
public class SalaryCalculator {
private Map<FUNCTION, Calculator> handlers;
public SalaryCalculator() {
handlers = new HashMap<FUNCTION, Calculator>();
handlers.put(FUNCTION.SELLER, new SellerSalaryCalculator());
handlers.put(FUNCTION.MANAGER, new ManagerSalaryCalculator());
handlers.put(FUNCTION.ENGINEER, new EngineerSalaryCalculator());
handlers.put(FUNCTION.CONSULTANT, new ConsultantSalaryCalculator());
handlers.put(FUNCTION.OWNER, new OwnerSalaryCalculator());
}
public float calculate(Employee employee) {
float salary = 0;
Calculator calculator = handlers.get(employee.getFunction());
calculator.calculate(employee);
return salary;
}
}
The refactoring is done!

