Low-Level Design: Converting a non-linear code to a linear code

In this post, I am going to discuss a very basic design pattern. How to convert a non-linear code (such as if-else, switch-case) to a linear code.

Currently, almost all modern languages have if-else, switch-case which help us to execute specific code for a certain condition. Which make our program non-linear.

if (condition1) {
executeForCondition1(...);
} else if (condition2) {
executeForCondition2(...);
} else if (condition3) {
executeForCondition3(...);
} else {
executeForDefaultCase(...);
}
------------------------------------------------------------------switch (condition) {
case condition1:
executeForCondition1(...);
case condition2:
executeForCondition2(...);
case condition3:
executeForCondition3(...);
default:
executeForDefaultCase(...);
}

These conditional syntaxes make our program a little difficult to read (though it is not bad all the time).

This design pattern replaces the above code with only one line of code and hides all conditional operators. The code will look something like this:

executor.getHandlerFor(condition).execute(...);

Let's take a problem statement: A company want to design a system which assigns laptop to its employees. In this case, our condition is the employee’s department.

Normally using a conditional operator the code looks something like this.

switch (department) {
case Engineering:
employee.assign(macPro16);
case Sales:
employee.assign(LenovoIdeaPad);
case Operation:
employee.assign(macPro13);
default:
employee.assign(DellXPS);
}

Let refactor this to make it linear:

LaptopProvider.getLaptopFor(department).assignTo(employee);

Now only one-liner code replaced the whole conditional operator part. Let looks at how it is designed:

USEEmployee employee = new Employee(ENGINEERING);-----------------------------------------------------------------new LaptopProvider().getLaptopFor(employee.role).assignTo(employee); // This above line replaced the whole if else condition.-----------------------------------------------------------------LaptopProvider.javaclass LaptopProvider {
Map<Role, Laptop> roleLaptopMap;

public LaptopProvider() {
roleLaptopMap = new HashMap<>();
roleLaptopMap.put(ENGINEERING, new MACPro16());
roleLaptopMap.put(SALES, new LenovoIdeaPad());
roleLaptopMap.put(OPERATION, new MACPro13());
roleLaptopMap.put(MARKETING, new DellXPS());
}

Laptop getLaptopFor(Role role) {
return roleLaptopMap.get(role);
}
}
Laptop.javaclass LenovoIdeaPad implements Laptop {
@Override
public String getBrand() {
return "Lenovo Idea Pad";
}

@Override
public void assignTo(Employee employee) {
employee.assign(this);
}
}

class DellXPS implements Laptop {
@Override
public String getBrand() {
return "DELL XPS";
}

@Override
public void assignTo(Employee employee) {
employee.assign(this);
}
}

class MACPro16 implements Laptop {
@Override
public String getBrand() {
return "MAC Pro 16";
}

@Override
public void assignTo(Employee employee) {
employee.assign(this);
}
}

class MACPro13 implements Laptop {
@Override
public String getBrand() {
return "MAC Pro 13";
}

@Override
public void assignTo(Employee employee) {
employee.assign(this);
}
}

interface Laptop {
String getBrand();

void assignTo(Employee employee);
}
Employee.javaclass Employee {
Role role;

public Employee(Role role) {
this.role = role;
}

public void assign(Laptop lenovoIdeaPad) {

}
}

enum Role {
ENGINEERING, SALES, MARKETING, OPERATION
}

Basically, we are taking the help of Map data structure where we are setting conditions as key and keeping the executor object as the value.

Now, the question is “Can we use this pattern all the time?”. The answer is “No”. Most of the time, the if-else condition has only 2–3 conditions, which is simple. It will be over-engineering if you implement this pattern. But if there are many conditions or a scenario where the number of conditions will increase soon, you can implement this design pattern.

Thanks for reading this article and happy coding. 😊👨🏼‍💻