DEV Community

Tails128
Tails128

Posted on • Updated on

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!

Discussion (0)