Clean Code Tip: Avoid subtle duplication of code and logic

Duplication is not only about lines of code, but also about data usage and meaning.
Reducing it will help us minimize the impact of every change.
Take this class as an example:
1 2 3 |
<span class="token keyword">class</span> <span class="token class-name">BookShelf</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Book<span class="token punctuation">[</span><span class="token punctuation">]</span></span> myBooks <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Book<span class="token punctuation">[</span><span class="token punctuation">]</span></span> <span class="token punctuation">{</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Book</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"C# in depth"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Book</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"I promessi paperi"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> myBooks<span class="token punctuation">.</span>Length<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">IsEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> myBooks<span class="token punctuation">.</span>Length <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">HasElements</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> myBooks<span class="token punctuation">.</span>Length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Here, both Count
and IsEmpty
use the same logical way to check the length of the collection: by calling myBooks.Length
.
What happens if you have to change the myBooks
collection and replace the array of Books with a collection that does not expose the Length
property? You will have to replace the logic everywhere!
So, a better approach is to “centralize” the way to count the items in the collection in this way:
1 2 3 |
<span class="token keyword">class</span> <span class="token class-name">BookShelf</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Book<span class="token punctuation">[</span><span class="token punctuation">]</span></span> myBooks <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Book<span class="token punctuation">[</span><span class="token punctuation">]</span></span> <span class="token punctuation">{</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Book</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"C# in depth"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Book</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"I promessi paperi"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">int</span></span> <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> myBooks<span class="token punctuation">.</span>Length<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">IsEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">HasElements</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
If you will need to replace the myBooks data type, you will simply have to update the Count
method – everything else will be the same.
Also, HasElements
and IsEmpty
are a logical duplication. If they’re not necessary, you should remove one. Remove the one most used in its negative form: if you find lots of if(!HasElements())
, you should consider replacing it with if(IsEmpty())
: always prefer the positive form!
Yes, I know, this is an extreme example: it’s too simple. But think of a more complex class or data flow in which you reuse the same logical flow, even if you’re not really using the exact same lines of code.
By duplicating the logic, you will need to write more tests that do the same thing. Also, it may happen that if you found a flaw in your logic, and you fix it in some places and forget to fix it in other methods.
Centralizing it will allow you to build safer code that is easier to test and update.
A simple way to avoid “logical” duplication? Abstract classes!
Well, there are many others… that I expect you to tell me in the comments section!
Happy coding!
🐧
Source: https://www.code4it.dev/cleancodetips/avoid-subtle-duplication
