I. Magic strings
Don’t lose it, re-use it
There’ll be a point in your programming life where you’ll come across string
data types. These are normally defined as double quoted "..."
pieces of text, like the examples below.
1
2
3
4
"foo"
"My message sentence"
"123"
"true"
As you can see, strings can be quite powerful, right? You can wrap the actual text in these, numbers and even boolean types.
The problem called
Magic strings
arises when you have code written where you check if one value, perhaps assigned in a variable, equals another value, perhaps written in the string.
For example, this small code snippet in isolation doesn’t look too offensive, right?
1
2
3
4
5
var coffeeName = "americano";
if (coffeeName == "latte")
{
return "Good choice with the Latte!";
}
Problem Statement
Now let’s consider slightly bigger piece of code where hopefully the magnitude of the problem will manifest itself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
namespace CoffeShop
{
public class CoffeeMaker
{
public int CoffeeSelector(string coffeeName)
{
switch (coffeeName)
{
case "americano": return 1;
case "latte": return 2;
case "cappuccino": return 3;
default: return 0;
}
}
public string MessagePrinter(string coffeeName)
{
switch (coffeeName)
{
case "americano": return "One Americano is coming right now sir!";
case "latte": return "Good choice with the Latte!";
case "cappucino": return "Nothing beats the good old Cappuccino!";
default: return "NA";
}
}
}
}
We can clearly spot a lot of strings
in the code above. We have coffee names duplication, as well as some hard-coded string responses. Imagine this being just a snippet of a bigger class or part of a project where these coffee names are scattered everywhere.
- Is this maintainable?
- How about having to replace a particular coffee name?
- Did you notice spelling mistake I made in “cappucino” case statement [line 22] inside of the MessagePrinter function?
- Imagine the time waste due to random code behaviours (bugs) resulting from mistakes like the one above?
Solution
Ideally, anything that you see duplicated
in your code should be defined once. This is acutely apparent when it comes to strings as these are effectively at the mercy of your copy & paste
skills to ensure any changes to these Magic strings
happen as expected.
However, we all know that the copy & paste is not the way serious coding should be done. Better approach would be to re-structure your code so that the Magic strings
are limited to ideally just once place where these are defined.
Therefore, let’s consider the example below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
namespace CoffeShop
{
public static class CoffeeHelper
{
public const string americano = "americano";
public const string latte = "latte";
public const string cappuccino = "cappuccino";
public static Dictionary<string, int> selector = new Dictionary<string, int>()
{
{ americano, 1 },
{ latte, 2 },
{ cappuccino, 3 }
};
public static Dictionary<string, string> messages = new Dictionary<string, string>()
{
{ americano, "One Americano is coming right now sir!" },
{ latte, "Good choice with the Latte!" },
{ cappuccino, "Nothing beats the good old Cappuccino!" }
};
}
public class CoffeeMaker
{
public int CoffeeSelector(string coffeeName)
{
return CoffeeHelper.selector[coffeeName];
}
public string MessagePrinter(string coffeeName)
{
return CoffeeHelper.messages[coffeeName];
}
}
}
With the changes above our code has now got simplified where Magic strings
are managed and defined in one place, the CoffeeHelper class [lines 3:22]. From that point onwards we reference our constants
in CoffeeMaker class [lines 24:35].
As an added bonus, that class is now serving a specific purpose in our codebase, it’s small enough to maintain and unit test.
Summary
And now if I had to fix that “cappucino” typo I only need to go to one place to do this and it’s auto-magically
reflected everywhere my cappuccino variable is referenced in the codebase.
Now, that’s what I call
Magic...