Abstract vs. DRY
We've talked about this before, including on our podcast and blog, but it bears repeating: DRY is the worst programming advice ever. It will almost always make your code harder to change as requirements evolve.
For example, say you have some code that uses a driver to manipulate a vending machine to prepare either coffee or tea. Like this:
Each of the methods called from the case
statement in the vend
method interacts with a hardware driver to prepare the recipes.
We can probably all agree on a few things about this code:
- The code is fairly readable.
- There's quite a bit of duplication between the two recipes.
- Future developers will likely add more beverages by adding more
when
clauses to thecase
statement. - At 25.1, the flog score for the
vend
method is already a little too high.
At this point, there are a couple of different paths forward:
- We could DRY the code.
- Or, we could introduce a layer of abstraction.
DRYing the code will simplify it on one axis. The Assignments, Branches, and Conditionals algorithm used by flog will produce a smaller number. Like this:
The vend
method now has a flog score of 16.1, which is nearly 10 points lower. Quite an improvement. But in choosing to DRY this code, we've tied the two recipes together, making it difficult to change one without affecting the other; and, we've made extending this code with additional recipes more difficult since we'll have to weave them together with the existing DRY recipes. Plus, there are at least 12 different code paths through the vend
method as it currently exists making the method difficult to test.
Or, we could go with adding a layer of abstraction, like this:
Now, there are more lines of code, and as such, the flog score for what used to be the vend
method is 35.0. However, the flog scores of the vend
method and the two recipe classes are 6.1, 15.2, and 10.8 respectively. So, since we can only work on one thing at a time, we're always working with less complexity. And, we've not commingled the recipes. So, they're still as easy to understand as the original implementation.
Furthermore, future developers are likely to add new recipes by creating new classes instead of interleaving new recipes into the existing DRY code. This separates the recipes and allows each to be tested independently making each portion of the system easier to test.
The astute observer might point out that the prepare
methods in the Coffee
and Tea
classes still contain duplication. These observers might even be tempted to DRY that duplication using something like the Template pattern. But, this would again intermingle the recipes. And, any new beverage would either have to comply with the template, or cause all of the algorithms to be rewritten unnecessarily.
This is the difference between DRY code and abstract code. DRY code might seem easier to write, but makes changing or extending an application harder. Abstract code may seem more difficult to write, but makes changing and extending the application simpler.
DRY is the easy solution. Abstraction is the simple solution.
Prefer simple over easy.
No spam, no sharing to third party. Only you and me.
Member discussion