Table based design vs Conditional Structures

July 03, 2018 0 Comments

Table based design vs Conditional Structures

 

 

Readable code is as important as functional code. While readability does not directly result in profits, it does affect developer’s productivity which might result in delayed shipping. Small details add up and before you know, your code base is too difficult to read and maintain.

One such readability issues that commonly occurs is long chains of conditional statements like if-else chains or switch case statements. This usually occurs when we have business logic involving comparisons with constants. The best bet would be to save such values in the database and retrieve them as and when needed as it is always considered easier to add records in a database through a script in the production rather than re-compiling the code and deploy it for a data change like what we are talking about here. In cases where this is not possible or you don’t want to fetch the values from cache or database every-time for constant data due to performance concerns or your constants are too generic to store in a database(like letters in the alphabet or digits), you would be more likely to store constants in the code itself.

I have first read about tabular based method from the book Code Complete by Steve McConnell(definitely worth your time if you are an aspiring developer) and made of use of it multiple times since then. The code used here will use C# but is generic enough to understand in general.

Table driven design is a programming paradigm in itself but this post focuses only on using it as an alternative for conditional structures. This can be used for things as simple as string comparisons to making decisions of which implementation of an interface to execute at run-time.

In this method, we define a table like structure, a hash table or a dictionary, which stores key-value pairs on which we wish to apply a certain condition. We loop through the table and apply the condition to each of its entries. When there are a lot of values that we need to compare with this would make the code more clear and concise because we don’t have to repeat the condition for each entry. Almost all the mainstream languages provide hash-based data structures and we need not worry about the performance as they usually have a O(1) time complexity.

Let us start with a simple example of string comparison.

We need to compare the given username with 15 mostly used names and return if any of them start with the given name. Writing the StartsWith method for 15 strings can be lengthy and difficult to maintain like shown below:

if (givenString.StartsWith("Abcd")) {
return true;
}
else if (givenString.StartsWith("efdg")) {
return true;
}
else if (givenString.StartsWith("jklm")) {
return true;
}
..........repeated 15 times

You may argue that it is not a big deal to add a new entry but imagine you want to modify the condition to check if the given string ends with(instead of starting with) any of the 15 most used words. 15 places needs to be changed and if we forget to update it at one of the places, a bug is introduced.

Now, the same code re-written with the table based design looks like shown below:

// Here the HashSet serves as the table to hold the constants.
HashSet<string> allValues = new HashSet<string>();
allValues.Add("Abcd");
allValues.Add("efdg");
allValues.Add("jklm");
.
.
.
foreach(var val in allValues) {
if(givenString.StartsWith(val)) {
return true;
}
}

This clearly is more concise and maintainable. When a modification is required we only have to replace StartsWith with EndsWith and we are done. Also, let us say you have decided to add 15 more names to your data, imagine how much easier it would be to just add them and not have to worry about the condition like shown in the second approach.

Let us look at another example where this approach can be used in conjunction with the https://en.wikipedia.org/wiki/Factorymethodpattern that decides which object to create based on a condition.

Suppose, we want to add to simulate the functionalities of different types of vehicles in our application. We define an interface IVehicle that represents the generic operations performed by all vehicles. We have declared only the method run here for simplicity and provided different implementations in Car, Truck and Bicycle classes. The : operator here indicates that the class Car implements the IVehicle interface.

public interface IVehicle {
void Run();
}
public class Car : IVehicle {
public void Run() {
Console.WriteLine("Running: Car");
}
}
public class Truck : IVehicle {
public void Run() {
Console.WriteLine("Running: Truck");
}
}
public class Bicycle : IVehicle {
public void Run() {
Console.WriteLine("Running: Bicycle");
}
}

To decide which object to use at run-time, we create a new factory class that returns the desired vehicle object on which the Run method is executed.

public class VehicleFactory {
 public IVehicle getVehicle(String vehicleName) {
if (vehicleName.Equals("Car")) {
return new Car();
}
else if (vehicleName.Equals("Truck")) {
return new Truck();
}
else if (vehicleName.Equals("Bicycle")) {
return new Bicycle();
}
return null;
}
}

Let us rewrite the factory class with the table based approach,

public class VehicleFactory {
Dictionary<string, IVehicle> vehicleObjects = new Dictionary<string, IVehicle>();
   public IVehicle getVehicle(String vehicleName) {
    vehicleObjects.Add("Car", new Car()); 
vehicleObjects.Add("Truck", new Truck());
vehicleObjects.Add("Bicycle", new Bicycle());

return vehicleObjects[vehicleName];
}
}

In many cases, the conditional statements are sufficient. In fact, it might be an overkill to use the table based design if the conditionals are simple and the data on which they operate on does not grow. But, if the data has the potential to grow as time passes and the code becomes too complex to understand it makes sense to replace them with the table based approach.


Tag cloud