Using Dictionaries to refactor

tails128

Tails128

Posted on May 18, 2020

Using Dictionaries to refactor

About one year and a half ago, I joined an awesome team of developers which had set up a very efficient environment from which I have learned a lot.

One of the many ideas which arose in such a thriving environment is to use Dictionaries as configuration files. In order to explain what I mean, and let's start from some bad code:

switch(foo.fooType) {
  case fooType.a: {
    bar(anEnum.aValue, foo, true);
  }
  case fooType.b: {
    bar(anEnum.bValue, foo, true);
  }
  case fooType.c: {
    bar(anEnum.cValue, foo, true);
  }
  default: {
    throw new ArgumentOutOfRangeException();
  }
}
Enter fullscreen mode Exit fullscreen mode

Just by looking at this code it looks like it is very complex: in the switch we are calling a function depending on foo... but this is not true!
What is actually happening is that we are just configuring the first parameter (anEnum).

Let's fix this

anEnum myValue;
switch(foo.fooType) {
  case fooType.a: {
    myValue = anEnum.aValue
  }
  case fooType.b: {
    myValue = anEnum.bValue
  }
  case fooType.c: {
    myValue = anEnum.cValue
  }
  default: {
    throw new ArgumentOutOfRangeException();
  }
}

bar(myValue, foo, true);
Enter fullscreen mode Exit fullscreen mode

This surely looks more understandable: it is immediate to see that we are not calling different functions, but we are just using a different value for myValue... but we can improve it a bit more with the use of a Dictionary.

fooConfigurator.cs:

public static Dictionary<fooType, anEnum> fooConfigurator = new Dictionary<fooType, anEnum>() {
  { fooType.a, anEnum.aValue },
  { fooType.b, anEnum.bValue },
  { fooType.c, anEnum.cValue },
}
Enter fullscreen mode Exit fullscreen mode

ourFile.cs

if(!fooConfigurator.Contains(foo.fooType){
  throw new ArgumentOutOfRangeException();  
}
anEnum myValue = fooConfigurator.get(foo.fooType);
bar(myValue, foo, true);
Enter fullscreen mode Exit fullscreen mode

This allows for some benefits in our code:

  1. O(1) access time instead of O(n) if you are using interpreted languages (partially compiled languages as java or c# and compiled languages usually adopt access tables, making it already O(1)... compilers are awesome!)
  2. The code is more readable: we can clearly see we just configure myValue depending on foo.fooType, and nothing else.
  3. This allows for simple configuration by updating our dictionary.

Can we do more with dictionaries?

Sure!
The cool thing about using Dictionaries as configuration files is that, if you use it with care, you can also pass functions or multiple parameters (just use objects) with a dictionary, allowing for a lot of flexibility!

What do you think about this approach? Let me know in the comments!

💖 💪 🙅 🚩
tails128
Tails128

Posted on May 18, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Code Smell | Magic Numbers
refactorit Code Smell | Magic Numbers

August 16, 2022

Top 10 practices I hate in C++
cpp Top 10 practices I hate in C++

September 29, 2020

When Should You Refactor Your Code?
refactorit When Should You Refactor Your Code?

August 13, 2020

Using Dictionaries to refactor
refactorit Using Dictionaries to refactor

May 18, 2020