<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>SOLID++ - Tag - Tracy Atteberry</title><link>https://tracyatteberry.com/tags/solid++/</link><description>SOLID++ - Tag - Tracy Atteberry</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>tracy@magicbydesign.com (Tracy Atteberry)</managingEditor><webMaster>tracy@magicbydesign.com (Tracy Atteberry)</webMaster><lastBuildDate>Wed, 01 Jan 2025 00:00:00 +0000</lastBuildDate><image><url>https://tracyatteberry.com/images/feed-icon.jpg</url><title>SOLID++ - Tag - Tracy Atteberry</title><link>https://tracyatteberry.com/tags/solid++/</link></image><atom:link href="https://tracyatteberry.com/tags/solid++/" rel="self" type="application/rss+xml"/><item><title>SOLID++: Encapsulate What Varies</title><link>https://tracyatteberry.com/posts/ewv/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate><author>Tracy Atteberry</author><guid>https://tracyatteberry.com/posts/ewv/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://tracyatteberry.com/posts/ewv/cocoon.jpg" referrerpolicy="no-referrer">
            </div><h3 id="introduction-organizing-your-tools">Introduction: Organizing Your Tools</h3>
<p>Ever notice how the messiest parts of your code are often where you&rsquo;re
handling different cases or variations? Maybe it&rsquo;s different file formats,
payment methods, or shipping calculations. That&rsquo;s exactly what we&rsquo;re going to
tackle today with one of the most practical object-oriented design principles:
&lsquo;Encapsulate what varies.&rsquo;</p>
<p>Think of it like organizing your tools. Instead of having screwdrivers and
wrenches scattered throughout the shop and house, you put them all in one
toolbox. Now, when you need to change or add a tool, you know exactly where
to go. That&rsquo;s what we mean by encapsulation in code – taking the parts that
might change and putting them in a well-defined place. Okay, yes. There is
that one screwdriver that always ends up in the kitchen drawer, but sometimes
a little duplication is better than an unhappy spouse.</p>
<p>In this article, we&rsquo;ll look at how this principle can transform messy,
hard-to-maintain code into something clean and flexible. I&rsquo;ll show you real
examples in Ruby, and by the end, you&rsquo;ll have a new tool for keeping your code
organized and easy to change.</p>
<h3 id="the-core-principle-explained">The Core Principle Explained</h3>
<p>Encapsulate what varies is all about isolating the parts of your code that
might change. It&rsquo;s a simple idea, but it can have a big impact on how
you structure your code. By putting the things that vary in one place,
you make your code more flexible and easier to maintain.</p>
<p>Imagine you&rsquo;re working on an woodworking platform that sells many types of
dressers. Each dresser can be of a different type and have a different assembly
process for one kind of dresser. If you scatter these variations throughout
your code, or conflate them with other code you&rsquo;ll end up with a tangled mess
that&rsquo;s hard to understand and change.</p>
<p>By encapsulating what varies, you can isolate these differences in one place.
This makes your code easier to read, test, and extend. When you need to add a
new dresser type or change the assembly logic, you know exactly where to look.</p>
<h3 id="a-practical-example">A Practical Example</h3>
<blockquote>
<p>🔍 In this post, we&rsquo;ll use Ruby 3.1 for code examples with endless methods
for brevity, but the principles here apply to any object-oriented language.</p>
</blockquote>
<p>Here&rsquo;s a simple example to illustrate the concept. Suppose you have a class
that builds a dresser based on the dresser type. You could write a single
method that handles all these variations:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DresserBuilder</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">construct</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">type</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="ss">:armoire</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span> <span class="o">=</span> <span class="no">Armoire</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="ss">:highboy</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span> <span class="o">=</span> <span class="no">Highboy</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="ss">:bombe</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span> <span class="o">=</span> <span class="no">Bombe</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">ArgumentError</span><span class="p">,</span> <span class="s2">&#34;Unknown dresser type: </span><span class="si">#{</span><span class="n">type</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">select_wood</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">cut_pieces</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">type</span> <span class="o">==</span> <span class="ss">:armoire</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span><span class="o">.</span><span class="n">join_traditionally</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span><span class="o">.</span><span class="n">join_with_screws</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">finish</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Dresser built!&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">armoire</span> <span class="o">=</span> <span class="no">DresserBuilder</span><span class="o">.</span><span class="n">construct</span><span class="p">(</span><span class="ss">:armoire</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Dresser built!</span></span></span></code></pre></div></div>
<p>This code works, but it&rsquo;s not very flexible. What if you need to add a new
dresser type or change the assembly process for one kind of dresser? You&rsquo;d have
to modify this method, which violates the Open/Closed Principle of SOLID.</p>
<p>Let&rsquo;s apply the &rsquo;encapsulate what varies&rsquo; principle to this code. We&rsquo;ll create
a separate<code>DresserFactory</code> class to handle the creation of different dresser
types. This class will encapsulate the choice of dresser type and return the
appropriate dresser object:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DresserFactory</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">make</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">type</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="ss">:armoire</span>
</span></span><span class="line"><span class="cl">      <span class="no">Armoire</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="ss">:highboy</span>
</span></span><span class="line"><span class="cl">      <span class="no">Highboy</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="ss">:bombe</span>
</span></span><span class="line"><span class="cl">      <span class="no">Bombe</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">ArgumentError</span><span class="p">,</span> <span class="s2">&#34;Unknown dresser type: </span><span class="si">#{</span><span class="n">type</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DresserBuilder</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">construct</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span> <span class="o">=</span> <span class="no">DresserFactory</span><span class="o">.</span><span class="n">make</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">select_wood</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">cut_pieces</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">type</span> <span class="o">==</span> <span class="ss">:armoire</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span><span class="o">.</span><span class="n">join_traditionally</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">dresser</span><span class="o">.</span><span class="n">join_with_screws</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">finish</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Dresser built!&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">armoire</span> <span class="o">=</span> <span class="no">DresserBuilder</span><span class="o">.</span><span class="n">construct</span><span class="p">(</span><span class="ss">:armoire</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Dresser built!</span></span></span></code></pre></div></div>
<p>Now we can easily add new types without modifying the <code>Dresser</code> class. This is
better, but what if the assembly process varies between dresser types? You&rsquo;d
have to modify the <code>Dresser</code> class again.</p>
<h4 id="strategy-pattern">Strategy Pattern</h4>
<p>Let&rsquo;s take it a step further and use the Strategy pattern to encapsulate the
assembly process in separate classes. This way, each dresser type can have its
own assembly logic, and we can easily change that without changing the
<code>Dresser</code> class:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DresserFactory</span>
</span></span><span class="line"><span class="cl">  <span class="no">BLUEPRINTS</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="ss">armoire</span><span class="p">:</span> <span class="no">Armoire</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">highboy</span><span class="p">:</span> <span class="no">Highboy</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">bombe</span><span class="p">:</span> <span class="no">Bombe</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span><span class="o">.</span><span class="n">freeze</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">make</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">BLUEPRINTS</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">      <span class="k">raise</span> <span class="no">ArgumentError</span><span class="p">,</span> <span class="s2">&#34;Unknown dresser type: </span><span class="si">#{</span><span class="n">type</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Assembleable</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">assemble</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AbstractDresser</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">Assembleable</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Armoire</span> <span class="o">&lt;</span> <span class="no">AbstractDresser</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">assemble</span>
</span></span><span class="line"><span class="cl">    <span class="n">select_wood</span>
</span></span><span class="line"><span class="cl">    <span class="n">cut_pieces</span>
</span></span><span class="line"><span class="cl">    <span class="n">join_traditionally</span>
</span></span><span class="line"><span class="cl">    <span class="n">finish_with_lacquer</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">select_wood</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">&#34;Selecting wood for armoire&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">cut_pieces</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">&#34;Cutting pieces for armoire&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join_traditionally</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">&#34;Assembling armoire traditionally&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">finish_with_lacquer</span> <span class="o">=</span> <span class="nb">puts</span> <span class="s2">&#34;Finishing armoire with lacquer&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DresserBuilder</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">construct</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span> <span class="o">=</span> <span class="no">DresserFactory</span><span class="o">.</span><span class="n">make</span><span class="p">(</span><span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span><span class="o">.</span><span class="n">assemble</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;Dresser built!&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dresser</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">armoire</span> <span class="o">=</span> <span class="no">DresserBuilder</span><span class="o">.</span><span class="n">construct</span><span class="p">(</span><span class="ss">:armoire</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Selecting wood for armoire</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Cutting pieces for armoire</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Assembling armoire traditionally</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Finishing armoire with lacquer</span>
</span></span><span class="line"><span class="cl"><span class="c1"># =&gt; Dresser built!</span></span></span></code></pre></div></div>
<p>Now, each dresser type has its own class, and the <code>Dresser</code> class delegates the
assembly process to the appropriate type. We&rsquo;ve also taken this opportunity to
update the <code>DresserFactory</code> class to trade the <code>case</code> statement for a hash
of blueprints for each dresser type. This way, adding a new type is as simple
as adding a new entry to the hash.</p>
<h3 id="common-patterns-that-apply-this-principle">Common Patterns That Apply This Principle</h3>
<p>The &rsquo;encapsulate what varies&rsquo; principle is closely related to several design
patterns that help you manage change in your code. Above, we used the Strategy
pattern to encapsulate the assembly process for different dresser types and the
Factory Method pattern to create instances of these types.</p>
<h3 id="tips-for-identifying-varying-elements">Tips for Identifying Varying Elements</h3>
<p>When applying the &rsquo;encapsulate what varies&rsquo; principle, it&rsquo;s essential to
identify the parts of your code that are likely to change. Here are some tips
to help you spot these elements:</p>
<ul>
<li><strong>Look for conditional logic</strong>: If you have a lot of <code>if</code> statements or
<code>case</code> blocks that handle different cases, those are good candidates for
encapsulation.</li>
<li><strong>Think about future requirements</strong>: Consider what might change in the future
and how you can isolate those changes.</li>
<li><strong>Separate concerns</strong>: If you have different concerns mixed together in one
class or method, try to separate them into distinct components.</li>
<li><strong>Code analysis</strong>: Use tools like static code analysis or code reviews to
identify parts of your code that change frequently.</li>
</ul>
<h3 id="common-pitfalls">Common Pitfalls</h3>
<p>While the &rsquo;encapsulate what varies&rsquo; principle is a powerful tool for managing
change, there are some common pitfalls to watch out for:</p>
<ul>
<li><strong>Overengineering</strong>: Don&rsquo;t go overboard and create unnecessary abstractions.
Keep it simple and refactor only when it makes sense.</li>
<li><strong>Premature optimization</strong>: Don&rsquo;t try to predict every possible change upfront.</li>
<li><strong>Ignoring SOLID principles</strong>: Remember that encapsulation is just one part
of good object-oriented design. Make sure your code follows other principles
like the Single Responsibility Principle and the Open/Closed Principle.</li>
<li><strong>Not testing variations</strong>: When you encapsulate what varies, make sure to
test each variation thoroughly. It&rsquo;s easy to introduce bugs when you refactor
code, so testing is crucial.</li>
</ul>
<p>Keep these pitfalls in mind as you apply this principle in your code. With
practice, you&rsquo;ll get better at identifying the parts that might change and
encapsulating them effectively.</p>
<h3 id="conclusion">Conclusion</h3>
<p>The &rsquo;encapsulate what varies&rsquo; principle is a powerful tool for managing change
in your code. By isolating the parts that are likely change, you make your code
more flexible, maintainable, and testable. Whether you&rsquo;re working on an
e-commerce platform, a game engine, or a woodworking platform, this principle
can help you keep your code organized and easy to extend.</p>
<p>And yes, sometimes that screwdriver will still end up in the kitchen drawer, but
sometimes that&rsquo;s just where it needs to be to maintain household harmony.</p>
<blockquote>
<p>🔍 This post is part of the <a href="/posts/solid_plus_plus" rel="">SOLID++</a> series where
we are exploring principles of object-oriented design beyond the five SOLID
principles.</p>
</blockquote>
]]></description></item><item><title>SOLID++: Law Of Demeter</title><link>https://tracyatteberry.com/posts/lod/</link><pubDate>Tue, 31 Dec 2024 00:00:00 +0000</pubDate><author>Tracy Atteberry</author><guid>https://tracyatteberry.com/posts/lod/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://tracyatteberry.com/posts/lod/nosy_bull.jpg" referrerpolicy="no-referrer">
            </div><div align="center">

</div>
<h3 id="introduction-getting-to-know-demeter">Introduction: getting to know Demeter</h3>
<p>Have you ever looked at your code and thought &lsquo;Why does this class need to know
about that other class&rsquo;s internal workings?&rsquo; Such dependencies can make code
difficult to understand, maintain, and test. The Law of Demeter, also known as
the principle of least knowledge, offers a way out of this complexity. Let&rsquo;s
explore how this principle can help you write cleaner, more maintainable code.</p>
<blockquote>
<p>🔍 This post is part of the <a href="/posts/solid_plus_plus" rel="">SOLID++</a> series where
we are exploring principles of object-oriented design beyond the five SOLID
principles.</p>
</blockquote>
<h3 id="what-is-the-law-of-demeter">What is the Law of Demeter?</h3>
<p>The Law of Demeter suggests that a given object should only interact with its
immediate collaborators and not reach through them to interact with other
objects&rsquo; methods or data. This helps reduce coupling and increase modularity in
code. Think of how you interact with a restaurant - you talk to the waiter, not
the chef directly. The Law of Demeter works similarly in code.</p>
<h3 id="why-follow-demeter">Why follow Demeter?</h3>
<p>Following Demeter&rsquo;s law offers several benefits:</p>
<ul>
<li>It makes your code more modular and easier to understand</li>
<li>It reduces coupling between objects</li>
<li>It simplifies refactoring and maintenance</li>
</ul>
<p>Now let&rsquo;s roll up our sleeves and look at some code that needs help.</p>
<blockquote>
<p>🔍 In this post, we&rsquo;ll use Ruby 3.1 for code examples with endless
methods for brevity, but the principles of Demeter apply to any
object-oriented language.</p>
</blockquote>
<h3 id="common-violations-spotting-the-problem">Common violations: spotting the problem</h3>
<p>Let&rsquo;s look at an example of a Demeter violation:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Engine</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="s1">&#39;Engine started!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Car</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:engine</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@engine</span> <span class="o">=</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">car</span> <span class="o">=</span> <span class="no">Car</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Engine</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">car</span><span class="o">.</span><span class="n">engine</span><span class="o">.</span><span class="n">start</span>  <span class="c1"># Violation</span></span></span></code></pre></div></div>
<p>Can you spot the violation here? The violation is that the <code>Car</code> class is
directly accessing the <code>Engine</code> object&rsquo;s <code>start</code> method. This breaks Demeter&rsquo;s
law by reaching too far into the <code>Engine</code> object.</p>
<h3 id="common-violations-fixing-the-problem">Common violations: fixing the problem</h3>
<p>Let&rsquo;s fix this violation by introducing a method in the <code>Car</code> class to start
the engine:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Engine</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="s1">&#39;Engine started!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Car</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@engine</span> <span class="o">=</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_engine</span> <span class="o">=</span> <span class="n">engine</span><span class="o">.</span><span class="n">start</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">car</span> <span class="o">=</span> <span class="no">Car</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Engine</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">car</span><span class="o">.</span><span class="n">start_engine</span>  <span class="c1"># Violation fixed</span></span></span></code></pre></div></div>
<p>In this refactored version, we&rsquo;ve introduced a <code>start_engine</code> method in the <code>Car</code>
class that delegates the call to the <code>Engine</code> object. This way, we avoid
violating Demeter&rsquo;s law.</p>
<h3 id="another-example-spotting-the-problem">Another example: spotting the problem</h3>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Project</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:manager</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">manager</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@manager</span> <span class="o">=</span> <span class="n">manager</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Manager</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:team</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">team</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@team</span> <span class="o">=</span> <span class="n">team</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">lead</span> <span class="o">=</span> <span class="s2">&#34;Alice&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">project</span> <span class="o">=</span> <span class="no">Project</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Manager</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Team</span><span class="o">.</span><span class="n">new</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">project</span><span class="o">.</span><span class="n">manager</span><span class="o">.</span><span class="n">team</span><span class="o">.</span><span class="n">lead</span>  <span class="c1"># Violation</span></span></span></code></pre></div></div>
<p>When you are reviewing code, look for long chains of method calls like this
(aka, &ldquo;dot chains&rdquo;). They are often the code smell for Demeter violations.
In this example, the <code>Project</code> class is directly accessing the <code>lead</code> method of
the <code>Team</code> object through the <code>Manager</code> object. This is another violation of
Demeter&rsquo;s law.</p>
<h3 id="another-example-fixing-the-problem">Another example: fixing the problem</h3>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Project</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">manager</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@manager</span> <span class="o">=</span> <span class="n">manager</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">team_lead</span> <span class="o">=</span> <span class="vi">@manager</span><span class="o">.</span><span class="n">team_lead</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Manager</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">team</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@team</span> <span class="o">=</span> <span class="n">team</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">team_lead</span> <span class="o">=</span> <span class="vi">@team</span><span class="o">.</span><span class="n">lead</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">lead</span> <span class="o">=</span> <span class="s2">&#34;Alice&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">project</span> <span class="o">=</span> <span class="no">Project</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Manager</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Team</span><span class="o">.</span><span class="n">new</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">project</span><span class="o">.</span><span class="n">team_lead</span>  <span class="c1"># No violation</span></span></span></code></pre></div></div>
<p>In this corrected version, we&rsquo;ve introduced a <code>team_lead</code> method in the <code>Project</code>
class that delegates the call to the <code>Team</code> object through the <code>Manager</code> object.
This way, we avoid violating Demeter&rsquo;s law by better encapsulating the behavior.</p>
<h3 id="using-rubys-built-in-tools">Using Ruby&rsquo;s built-in tools</h3>
<p>In the previous examples, we could have used Ruby&rsquo;s <code>forwardable</code> module to
delegate the method call. This is a common pattern in Ruby to avoid Demeter
violations. Let&rsquo;s refactor the previous example using <code>forwardable</code>:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;forwardable&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Project</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">Forwardable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">def_delegator</span> <span class="ss">:@manager</span><span class="p">,</span> <span class="ss">:team_lead</span><span class="p">,</span> <span class="ss">:team_lead</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">manager</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@manager</span> <span class="o">=</span> <span class="n">manager</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Manager</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">Forwardable</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">def_delegator</span> <span class="ss">:@team</span><span class="p">,</span> <span class="ss">:lead</span><span class="p">,</span> <span class="ss">:team_lead</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">team</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@team</span> <span class="o">=</span> <span class="n">team</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">lead</span> <span class="o">=</span> <span class="s2">&#34;Alice&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">project</span> <span class="o">=</span> <span class="no">Project</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Manager</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Team</span><span class="o">.</span><span class="n">new</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">project</span><span class="o">.</span><span class="n">team_lead</span>  <span class="c1"># No violation</span></span></span></code></pre></div></div>
<p>In this refactored version, we&rsquo;ve used Ruby&rsquo;s <code>forwardable</code> module to delegate
the <code>team_lead</code> method call from the <code>Project</code> class to the <code>Team</code> object through
the <code>Manager</code> object. This is a clean and concise way to avoid Demeter violations.</p>
<h3 id="demeter-on-rails-the-delegate-method">Demeter on Rails: the <code>delegate</code> method</h3>
<p>Rails <code>ActiveSupport</code> provides a bit of syntactic sugar to make following Demeter
even easier. Let&rsquo;s use the <code>delegate</code> method to refactor the previous example:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Project</span>
</span></span><span class="line"><span class="cl">  <span class="n">delegate</span> <span class="ss">:team_lead</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="ss">:@manager</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">manager</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@manager</span> <span class="o">=</span> <span class="n">manager</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Manager</span>
</span></span><span class="line"><span class="cl">  <span class="n">delegate</span> <span class="ss">:team_lead</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="ss">:@team</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">team</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@team</span> <span class="o">=</span> <span class="n">team</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">team_lead</span> <span class="o">=</span> <span class="s2">&#34;Alice&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">project</span> <span class="o">=</span> <span class="no">Project</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Manager</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Team</span><span class="o">.</span><span class="n">new</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">project</span><span class="o">.</span><span class="n">team_lead</span>  <span class="c1"># No violation</span></span></span></code></pre></div></div>
<p>The <code>delegate</code> method allows us to easily forward method calls to associated
objects, keeping our Rails code clean and readable.</p>
<h3 id="exceptions-to-the-rule">Exceptions to the rule</h3>
<p>While Demeter is generally a good practice, there are situations where you
might need to bend the rules:</p>
<h4 id="when-working-with-external-apis-or-libraries">When working with external APIs or libraries</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">WeatherService</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">temperature</span> <span class="o">=</span> <span class="no">WeatherAPI</span><span class="o">.</span><span class="n">get_temperature</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">WeatherAPI</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">get_temperature</span> <span class="o">=</span> <span class="mi">25</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">service</span> <span class="o">=</span> <span class="no">WeatherService</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">service</span><span class="o">.</span><span class="n">temperature</span></span></span></code></pre></div></div>
<p>Why does this not violate Demeter? Because the <code>WeatherService</code> class is directly
accessing the <code>get_temperature</code> method of the <code>WeatherAPI</code> class. In this case,
it might be acceptable due to the nature of the external API.</p>
<h4 id="when-working-with-data-structures-like-hashes-or-arrays">When working with data structures like hashes or arrays</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">WeatherService</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">temperature</span> <span class="o">=</span> <span class="no">WeatherAPI</span><span class="o">.</span><span class="n">weather_data</span><span class="o">[</span><span class="ss">:temperature</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">WeatherAPI</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">weather_data</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">temperature</span><span class="p">:</span> <span class="mi">25</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">service</span> <span class="o">=</span> <span class="no">WeatherService</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">service</span><span class="o">.</span><span class="n">temperature</span></span></span></code></pre></div></div>
<p>Why does this not violate Demeter? Because the <code>WeatherService</code> class is directly
accessing the <code>temperature</code> attribute of the <code>weather_data</code> hash. In this case,
it might be acceptable due to the nature of the data structure.</p>
<h4 id="in-cases-of-immutability">In cases of immutability</h4>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:name</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Greeting</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">greet</span> <span class="o">=</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;Alice&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">greeting</span> <span class="o">=</span> <span class="no">Greeting</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">greeting</span><span class="o">.</span><span class="n">greet</span></span></span></code></pre></div></div>
<p>Why does this not violate Demeter? Because the <code>Greeting</code> class is directly
accessing the <code>name</code> attribute of the <code>User</code> object. In this case, it might be
acceptable due to the immutability of the <code>User</code> object.</p>
<h4 id="with-certain-design-patterns-like-builder">With certain design patterns like Builder</h4>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:name</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserBuilder</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:user</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">with_name</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">name</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">build</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">builder</span> <span class="o">=</span> <span class="no">UserBuilder</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">user</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="n">with_name</span><span class="p">(</span><span class="s2">&#34;Alice&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">build</span>
</span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span></span></span></code></pre></div></div>
<p>Why does this violate Demeter? Because the <code>UserBuilder</code> class is directly
accessing the <code>name</code> attribute of the <code>User</code> object. In this case, it might be
acceptable due to the nature of the Builder pattern. That is, the <code>UserBuilder</code>
class is responsible for constructing the <code>User</code> object.</p>
<h3 id="testing-benefits">Testing benefits</h3>
<p>Following Demeter&rsquo;s law can make your code easier to test. By reducing the
coupling between objects, you can write more focused unit tests that don&rsquo;t need
to know about the internal details of other objects. This makes your tests more
resilient to changes and easier to maintain. For example:</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Engine</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="s1">&#39;Engine started!&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Car</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@engine</span> <span class="o">=</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_engine</span> <span class="o">=</span> <span class="n">engine</span><span class="o">.</span><span class="n">start</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Test</span>
</span></span><span class="line"><span class="cl"><span class="no">RSpec</span><span class="o">.</span><span class="n">describe</span> <span class="no">Car</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">it</span> <span class="s1">&#39;starts the engine&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">engine</span> <span class="o">=</span> <span class="n">instance_double</span><span class="p">(</span><span class="no">Engine</span><span class="p">,</span> <span class="ss">start</span><span class="p">:</span> <span class="s1">&#39;Engine started!&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">car</span> <span class="o">=</span> <span class="no">Car</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">expect</span><span class="p">(</span><span class="n">car</span><span class="o">.</span><span class="n">start_engine</span><span class="p">)</span><span class="o">.</span><span class="n">to</span> <span class="n">eq</span><span class="p">(</span><span class="s1">&#39;Engine started!&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>In this test, we&rsquo;re able to mock the <code>Engine</code> object and focus on testing the
<code>Car</code> class in isolation. This is possible because the <code>Car</code> class follows
Demeter&rsquo;s law and doesn&rsquo;t reach into the <code>Engine</code> object.</p>
<h3 id="common-pitfalls-when-to-be-cautious">Common pitfalls: when to be cautious</h3>
<p>While Demeter is a powerful principle, there are some common pitfalls to be
aware of:</p>
<ul>
<li><strong>Overuse of delegation</strong>: Be cautious of overusing delegation, as it can lead
to excessive method chaining and complexity.</li>
<li><strong>Excessive abstraction</strong>: Avoid creating unnecessary layers of abstraction
just to follow Demeter. Sometimes direct access is more readable and
maintainable.</li>
<li><strong>Performance concerns</strong>: Excessive delegation can lead to performance issues
due to the overhead of method calls.</li>
<li><strong>Contextual understanding</strong>: Sometimes direct access to an object&rsquo;s methods
can provide better context and understanding of the code.</li>
<li><strong>Code readability</strong>: While Demeter aims to improve code readability, excessive
delegation can sometimes have the opposite effect.</li>
</ul>
<p>To avoid these pitfalls, use your best judgment and consider the specific
requirements of your codebase. While being careful not to overcomplicate things
in the name of following Demeter, also don&rsquo;t use these pitfalls as an excuse to
ignore the principle altogether.</p>
<h3 id="conclusion-putting-demeter-into-practice">Conclusion: Putting Demeter into Practice</h3>
<p>As you start applying the Law of Demeter, you&rsquo;ll notice your code becoming more
modular and easier to understand. Changes that once felt risky become
straightforward, and refactoring becomes less of a headache.</p>
<p>Remember, Demeter is not a strict rule but a guideline to help you write cleaner,
more maintainable code. Use it wisely, and your code will thank you.</p>
]]></description></item><item><title>SOLID++: Composition Over Inheritance</title><link>https://tracyatteberry.com/posts/coh/</link><pubDate>Wed, 25 Dec 2024 00:00:00 +0000</pubDate><author>Tracy Atteberry</author><guid>https://tracyatteberry.com/posts/coh/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://tracyatteberry.com/posts/coh/coh_blocks.png" referrerpolicy="no-referrer">
            </div><h3 id="introduction">Introduction</h3>
<p>Ever been stuck in a tangled inheritance hierarchy where changing one parent
class feels like playing software Jenga? You&rsquo;re not alone. I found myself in
that situation once while writing a font creation library. The rigid hierarchy
and fragile base class led to a brittle solution that was difficult to extend
and maintain. While inheritance is a powerful feature of object-oriented
programming, it&rsquo;s often not the best tool for the job. Let&rsquo;s explore a more
flexible alternative: composition.</p>
<p>As part of the <a href="/posts/solid_plus_plus" rel="">SOLID++</a> series, we are exploring principles
of object-oriented design beyond the five SOLID principles. Here we&rsquo;ll discuss the
idea of favoring composition over inheritance. First, we&rsquo;ll look at some different types
of composition and then dive into the reasons why it&rsquo;s often preferred over inheritance.
All code examples are written for Ruby 3.1. using endless methods for brevity.</p>
<h3 id="different-types-of-composition">Different types of composition</h3>
<p>Before diving into why composition is often better than inheritance, let&rsquo;s look
at seven common ways to compose objects. Each approach has its own
strengths and use cases.</p>
<h4 id="1-has-a-aggregation">1. Has-a aggregation</h4>
<p>This is where an object contains a reference to another object, but the
contained object can exist independently. Think of a Team that has
Players - the Players can exist even if the Team dissolves:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Player</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_reader</span> <span class="ss">:name</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Team</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@players</span> <span class="o">=</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">add_player</span><span class="p">(</span><span class="n">player</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@players</span> <span class="o">&lt;&lt;</span> <span class="n">player</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">t_rex</span> <span class="o">=</span> <span class="no">Player</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;T. Rex&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">raptors</span> <span class="o">=</span> <span class="no">Team</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">raptors</span><span class="o">.</span><span class="n">add_player</span><span class="p">(</span><span class="n">t_rex</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Use this when objects have independent lifecycles and loose coupling is desired.</p>
<h4 id="2-part-of-aggregation">2. Part-of aggregation</h4>
<p>Part-of aggregation is a stronger form of aggregation where the contained
object&rsquo;s lifecycle is tied to the container. This is useful when objects are
tightly coupled. Think of a Car and its Engine - the Engine only exists as part
of the Car:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Engine</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="s2">&#34;Let&#39;s go!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Car</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@engine</span> <span class="o">=</span> <span class="no">Engine</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="o">=</span> <span class="vi">@engine</span><span class="o">.</span><span class="n">start</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">car</span> <span class="o">=</span> <span class="no">Car</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">car</span><span class="o">.</span><span class="n">start</span>  <span class="c1"># =&gt; &#34;Let&#39;s go!&#34;</span></span></span></code></pre></div></div>
<p>Use this when objects are tightly coupled and the contained object can&rsquo;t exist
without the container.</p>
<h4 id="3-dependency-injection">3. Dependency injection</h4>
<p>This is where dependencies are passed in rather than created internally.
Useful for creating flexlible, testable code.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PaymentProcessor</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">payment_gateway</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@payment_gateway</span> <span class="o">=</span> <span class="n">payment_gateway</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">process_payment</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@payment_gateway</span><span class="o">.</span><span class="n">charge</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Different gateways can be injected</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">StripeGateway</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># This is pseudo-code and the actual API may be different</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">charge</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span> <span class="o">=</span> <span class="no">Stripe</span><span class="o">::</span><span class="no">Charge</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="ss">amount</span><span class="p">:</span> <span class="n">amount</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PayPalGateway</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># This is pseudo-code and the actual API may be different</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">charge</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span> <span class="o">=</span> <span class="no">PayPal</span><span class="o">::</span><span class="no">Charge</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="ss">amount</span><span class="p">:</span> <span class="n">amount</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">processor</span> <span class="o">=</span> <span class="no">PaymentProcessor</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">StripeGateway</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">processor</span><span class="o">.</span><span class="n">process_payment</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span></span></span></code></pre></div></div>
<p>Use this when you want to decouple components and make them easier to test.</p>
<h4 id="4-delegation">4. Delegation</h4>
<p>Ruby has built-in support for delegation with <code>forwardable</code>.
Used to forward messages while maintaining encapsulation.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;forwardable&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Writer</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">Forwardable</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="n">def_delegators</span> <span class="ss">:@formatter</span><span class="p">,</span> <span class="ss">:format_text</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@formatter</span> <span class="o">=</span> <span class="n">formatter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">HTMLFormatter</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">format_text</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="o">=</span> <span class="s2">&#34;&lt;p&gt;</span><span class="si">#{</span><span class="n">text</span><span class="si">}</span><span class="s2">&lt;/p&gt;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">html_writer</span> <span class="o">=</span> <span class="no">Writer</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">HTMLFormatter</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">html_writer</span><span class="o">.</span><span class="n">format_text</span><span class="p">(</span><span class="s2">&#34;Hello, world!&#34;</span><span class="p">)</span>  <span class="c1"># =&gt; &#34;&lt;p&gt;Hello, world!&lt;/p&gt;&#34;</span></span></span></code></pre></div></div>
<p>Use this when you want to forward messages to another object while maintaining
encapsulation.</p>
<h4 id="5-roletrait-composition-similar-to-mixins-but-more-flexible">5. Role/trait composition (similar to mixins but more flexible)</h4>
<p>Used for the additions of dynamic behavior.
This is where objects can gain or lose behaviors at runtime:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Swimming</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">swim</span> <span class="o">=</span> <span class="s2">&#34;swimming&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">Flying</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fly</span> <span class="o">=</span> <span class="s2">&#34;flying&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Bird</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@behaviors</span> <span class="o">=</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">add_behavior</span><span class="p">(</span><span class="n">behavior</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kp">extend</span><span class="p">(</span><span class="n">behavior</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@behaviors</span> <span class="o">&lt;&lt;</span> <span class="n">behavior</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="c1"># Note: In real code, you&#39;d need a more sophisticated way to remove modules</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># as this would only remove the behavior from the array and not undo the extend</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># operation.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">remove_behavior</span><span class="p">(</span><span class="n">behavior</span><span class="p">)</span> <span class="o">=</span> <span class="vi">@behaviors</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">behavior</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">duck</span> <span class="o">=</span> <span class="no">Bird</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">duck</span><span class="o">.</span><span class="n">add_behavior</span><span class="p">(</span><span class="no">Swimming</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">duck</span><span class="o">.</span><span class="n">add_behavior</span><span class="p">(</span><span class="no">Flying</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">duck</span><span class="o">.</span><span class="n">swim</span>  <span class="c1"># =&gt; &#34;swimming&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">duck</span><span class="o">.</span><span class="n">fly</span>   <span class="c1"># =&gt; &#34;flying&#34;</span></span></span></code></pre></div></div>
<p>Use this when you want to add or remove behaviors dynamically.</p>
<h4 id="6-strategy-pattern">6. Strategy pattern</h4>
<p>This is similar to dependency injection but focused on encapsulating
algorithms. This is one of the more commonly used patterns in real-world
applications.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PricingStrategy</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">calculate_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">)</span> <span class="o">=</span> <span class="k">raise</span> <span class="no">NotImplementedError</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RegularPricing</span> <span class="o">&lt;</span> <span class="no">PricingStrategy</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">calculate_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">)</span> <span class="o">=</span> <span class="n">base_price</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DiscountPricing</span> <span class="o">&lt;</span> <span class="no">PricingStrategy</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">calculate_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">base_price</span> <span class="o">*</span> <span class="mi">0</span><span class="o">.</span><span class="mi">9</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Product</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">pricing_strategy</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@pricing_strategy</span> <span class="o">=</span> <span class="n">pricing_strategy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">price</span><span class="p">(</span><span class="n">base_price</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@pricing_strategy</span><span class="o">.</span><span class="n">calculate_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">product</span> <span class="o">=</span> <span class="no">Product</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">DiscountPricing</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">product</span><span class="o">.</span><span class="n">price</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>  <span class="c1"># =&gt; 9.0</span></span></span></code></pre></div></div>
<p>Notice that the example above appears to use inheritance, but the base class
<code>PricingStrategy</code> is an abstract class that defines an interface for concrete
strategies to implement. This is a form of composition because the <code>Product</code>
class is composed of a pricing strategy.</p>
<p>Use the strategy pattern when you want to encapsulate algorithms and make them
interchangeable.</p>
<h4 id="7-decorator-pattern">7. Decorator pattern</h4>
<p>This is where objects can be wrapped in other objects to add behaviors.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-ruby">
        <span class="code-title"><i class="arrow fas fa-chevron-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Coffee</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">cost</span> <span class="o">=</span> <span class="mi">3</span><span class="o">.</span><span class="mo">00</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">description</span> <span class="o">=</span> <span class="s2">&#34;Plain coffee&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MilkDecorator</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">coffee</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@coffee</span> <span class="o">=</span> <span class="n">coffee</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">cost</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@coffee</span><span class="o">.</span><span class="n">cost</span> <span class="o">+</span> <span class="mi">0</span><span class="o">.</span><span class="mi">50</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">description</span> <span class="o">=</span> <span class="p">(</span><span class="vi">@coffee</span><span class="o">.</span><span class="n">description</span> <span class="o">+</span> <span class="s2">&#34; with milk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Usage</span>
</span></span><span class="line"><span class="cl"><span class="n">coffee</span> <span class="o">=</span> <span class="no">Coffee</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">coffee_with_milk</span> <span class="o">=</span> <span class="no">MilkDecorator</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">coffee</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">coffee_with_milk</span><span class="o">.</span><span class="n">cost</span>  <span class="c1"># =&gt; 3.50</span></span></span></code></pre></div></div>
<p>Use the decorator pattern when you want to add behaviors to objects at runtime.</p>
<p>Now that we&rsquo;ve seen the various ways objects can work together through
composition, let&rsquo;s tackle the big question: Why should we usually prefer these
approaches over inheritance?</p>
<h3 id="favoring-composition-over-inheritance">Favoring composition over inheritance</h3>
<p>The core argument for composition over inheritance is that inheritance can lead
to tight coupling between parent and child classes. This can lead to several
problems:</p>
<h4 id="1-fragile-base-class-problem">1. Fragile base class problem</h4>
<p>When you change something in a parent class, it can unexpectedly break child
classes in ways that are hard to predict. For example, if you have a Bird
parent class and modify how its <code>fly()</code> method works, you might accidentally
break specific bird implementations that were relying on the old behavior.</p>
<h4 id="2-rigid-hierarchy">2. Rigid hierarchy</h4>
<p>Inheritance hierarchies are fixed at compile-time and can&rsquo;t be changed at
runtime. With composition, you can easily swap behaviors at runtime, making
your code more flexible.</p>
<h4 id="3-the-is-a-relationship-isnt-always-clear">3. The &ldquo;is-a&rdquo; relationship isn&rsquo;t always clear</h4>
<p>Sometimes what seems like an &ldquo;is-a&rdquo; relationship isn&rsquo;t actually appropriate.
The classic example is Square inheriting from Rectangle. While mathematically a
square is a rectangle, in code this can violate the Liskov Substitution
Principle because a square&rsquo;s width and height must always be equal.</p>
<h4 id="4-limited-to-single-inheritance">4. Limited to single inheritance</h4>
<p>In languages with single inheritance (like Java, Ruby, etc.), you&rsquo;re forced to
choose one parent class. With composition, you can combine multiple behaviors.</p>
<h4 id="5-testing-and-maintenance">5. Testing and maintenance</h4>
<p>Composition makes testing easier because you can mock or stub out components.
It also makes maintenance easier because changes are more localized and don&rsquo;t
ripple through an inheritance hierarchy.</p>
<h3 id="conclusion">Conclusion</h3>
<p>We&rsquo;ve covered a lot of ground in our exploration of composition! While
inheritance is a fundamental feature of object-oriented programming, we&rsquo;ve seen
how composition often provides more flexible, maintainable solutions. Let&rsquo;s
recap when to use each approach.</p>
<p>Choose composition when:</p>
<ul>
<li>You need runtime flexibility (like our Bird with swappable movement behaviors)</li>
<li>The relationship between objects is &ldquo;has-a&rdquo; rather than &ldquo;is-a&rdquo;</li>
<li>You want to keep your components loosely coupled</li>
<li>You need to combine multiple behaviors (remember our swimming, flying duck!)</li>
</ul>
<p>Stick with inheritance when:</p>
<ul>
<li>You have a true &ldquo;is-a&rdquo; relationship that won&rsquo;t change over time</li>
<li>The parent class is stable and well-designed</li>
<li>You&rsquo;re extending framework classes that expect inheritance</li>
<li>The child class is a proper behavioral subtype of the parent</li>
</ul>
<p>Think of composition as building with LEGO blocks instead of sculpting from a
single piece of clay. With LEGO, you can easily snap pieces together, rearrange
them, or swap them out. That flexibility is exactly what makes composition so
powerful in software design.</p>
<p>In our next article, we&rsquo;ll explore another powerful OOD principle: the Law of
Demeter. We&rsquo;ll see how this principle helps us write code that&rsquo;s more
maintainable by reducing dependencies between objects. Until then, try looking
at your current codebase - are there places where composition might work better
than inheritance?</p>
<p>Remember: Good design isn&rsquo;t about following rules blindly. It&rsquo;s about
understanding the trade-offs and choosing the right tool for the job. Sometimes
that tool will be inheritance, but more often than not, composition will give
you the flexibility you need to build maintainable, adaptable software.</p>
<hr>
<ul>
<li>Cover image <a href="http://www.freepik.com" target="_blank" rel="noopener noreffer ">designed by Freepik</a></li>
</ul>
]]></description></item><item><title>SOLID++: More Object-Oriented Design Principles</title><link>https://tracyatteberry.com/posts/solid_plus_plus/</link><pubDate>Mon, 02 Dec 2024 00:00:00 +0000</pubDate><author>Tracy Atteberry</author><guid>https://tracyatteberry.com/posts/solid_plus_plus/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://tracyatteberry.com/posts/solid_plus_plus/objects.jpg" referrerpolicy="no-referrer">
            </div><h3 id="introduction">Introduction</h3>
<p>If you&rsquo;ve been writing object-oriented code for a while, you&rsquo;ve probably heard
of the SOLID principles. These five guidelines have become so fundamental to OO
design that they&rsquo;re often treated as the complete playbook for writing good
code. But here&rsquo;s the thing - they&rsquo;re just the beginning.</p>
<h3 id="beyond-solid">Beyond SOLID</h3>
<p>The SOLID principles, introduced by Robert C. Martin (affectionately known as
&ldquo;Uncle Bob&rdquo;) in the early 2000s, are like the foundation of a well-built house.
They give us a stable base for creating code that&rsquo;s easier to maintain, extend,
and understand. You can find a good explanation of them in
<a href="https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/" target="_blank" rel="noopener noreffer ">this post</a>
at freeCodeCamp. Briefly, the SOLID principles are:</p>
<blockquote>
<p><strong>S</strong>ingle Responsibility Principle - A class should do one thing, and do it well<br/>
<strong>O</strong>pen/Closed Principle - Open for extension, closed for modification<br/>
<strong>L</strong>iskov Substitution Principle - Subtypes must be substitutable for their base types<br/>
<strong>I</strong>nterface Segregation Principle - Clients should not be forced to depend on interfaces they do not use<br/>
<strong>D</strong>ependency Inversion Principle - High-level modules should not depend on low-level modules; both should depend on abstractions</p>
</blockquote>
<p>While SOLID principles are fantastic tools for good design, they&rsquo;re part of a
larger toolbox. There are several other powerful OOD principles that can take
your code to the next level. In this series, we&rsquo;ll explore three of my
favorites:</p>
<ol>
<li><a href="/posts/coh" rel="">Composition Over Inheritance</a> - Learn when to favor object composition over class inheritance</li>
<li><a href="/posts/lod" rel="">Law of Demeter</a> - Discover how to write code that minds its own business</li>
<li><a href="/posts/ewv" rel="">Encapsulate What Varies</a> - Master the art of isolating change</li>
</ol>
<p>While SOLID principles provide an excellent foundation for object-oriented
design, they&rsquo;re just the beginning of your journey toward writing exceptional
code. In the upcoming posts, we&rsquo;ll explore these additional principles through
practical examples and real-world scenarios. By the end of this series, you&rsquo;ll
have new tools to help you write code that&rsquo;s not just solid - it&rsquo;s outstanding.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Stay tuned for our next post, where we&rsquo;ll dive into <a href="/posts/coh" rel="">Composition Over
Inheritance</a> and learn why sometimes it&rsquo;s better to have a friend
than a complex relationship with parents!</p>
<hr>
<ul>
<li>Cover image <a href="http://www.freepik.com" target="_blank" rel="noopener noreffer ">designed by pikisuperstar / Freepik</a></li>
</ul>
]]></description></item></channel></rss>