<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Jyotirmaya's Blog]]></title><description><![CDATA[I’m Jyotirmaya Das, a Professional Software Engineer who finds joy in all things Java. From Spring to Spring Boot and beyond, I’m here simply to share my love f]]></description><link>https://jyotirmayadas.in</link><generator>RSS for Node</generator><lastBuildDate>Tue, 26 May 2026 11:10:47 GMT</lastBuildDate><atom:link href="https://jyotirmayadas.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[An Overview of SOLID Principles]]></title><description><![CDATA[Let's look at each principle one by one. Following the SOLID acronym, they are:

Single Responsibility Principle

Open-Closed Principle

Liskov Substitution Principle

Interface Segregation Principle

Dependency Inversion Principle


Single Responsib...]]></description><link>https://jyotirmayadas.in/an-overview-of-solid-principles</link><guid isPermaLink="true">https://jyotirmayadas.in/an-overview-of-solid-principles</guid><category><![CDATA[Java]]></category><category><![CDATA[SOLID principles]]></category><dc:creator><![CDATA[Jyotirmaya Das]]></dc:creator><pubDate>Sat, 20 Apr 2024 06:14:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713592783797/a7d2bb3d-f625-47ff-9887-c77a56508d33.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's look at each principle one by one. Following the SOLID acronym, they are:</p>
<ol>
<li><p>Single Responsibility Principle</p>
</li>
<li><p>Open-Closed Principle</p>
</li>
<li><p>Liskov Substitution Principle</p>
</li>
<li><p>Interface Segregation Principle</p>
</li>
<li><p>Dependency Inversion Principle</p>
</li>
</ol>
<h2 id="heading-single-responsibility">Single Responsibility</h2>
<ul>
<li><p>It says each entity, class, or microservice should have <strong>one, well-defined purpose</strong>. While this purpose may involve multiple tasks or behaviors, it's crucial to set clear boundary for that <strong>purpose</strong> to define responsibilities within the entity or class.</p>
</li>
<li><p>For instance, consider the following contract (an interface in Java) from Spring Security Framework, Even though it has one responsibility, it includes two distinct behaviours: encoding and matching passwords.</p>
<pre><code class="lang-JAVA">  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PasswordEncoder</span> </span>{
      <span class="hljs-function">String <span class="hljs-title">encode</span><span class="hljs-params">(CharSequence rawPassword)</span></span>;

      <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">matches</span><span class="hljs-params">(CharSequence rawPassword, String encodedPassword)</span></span>;

      <span class="hljs-function"><span class="hljs-keyword">default</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">upgradeEncoding</span><span class="hljs-params">(String encodedPassword)</span> </span>{
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
      }
  }
</code></pre>
</li>
<li><p>Both behaviors fall within the same responsibility—the purpose of the password encoder.</p>
</li>
</ul>
<blockquote>
<p>In essence, the Single Responsibility Principle applies across various entities, ranging from individual classes, methods of classes to entire microservices. While these entities may perform multiple tasks, it's essential that these tasks align with a singular purpose. This purpose must be clearly defined with well-established boundaries such that we don't have to change it afterward. And this is where the second principle Open Closed comes into picture.</p>
</blockquote>
<h2 id="heading-open-closed">Open Closed</h2>
<ul>
<li><p>The Open-Closed Principle suggests that you should try not to change a responsibility rather you should be able to use the responsibility that you have to implement new functionality. Because when you change something that already exists, it means that you actually change the <strong>purpose</strong> that you initially defined.</p>
</li>
<li><p>That's why, when designing in object-oriented programming, your classes, and at the architecture level, your microservices take into consideration the responsibilities.</p>
</li>
<li><p>Consider the boundaries of your responsibilities and the purpose of the specific object you design. This aspect should not be changed afterward or should be changed as rarely as possible.</p>
</li>
<li><p>For instance, consider the following contract (an interface in Java) from Spring Security Framework again,</p>
<pre><code class="lang-JAVA">  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PasswordEncoder</span> </span>{
      <span class="hljs-function">String <span class="hljs-title">encode</span><span class="hljs-params">(CharSequence rawPassword)</span></span>;

      <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">matches</span><span class="hljs-params">(CharSequence rawPassword, String encodedPassword)</span></span>;

      <span class="hljs-function"><span class="hljs-keyword">default</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">upgradeEncoding</span><span class="hljs-params">(String encodedPassword)</span> </span>{
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
      }
  }
</code></pre>
<p>  Do we typically change the PasswordEncoder interface? No, it's something we obtain from the framework and utilize as is. It has its boundaries defined by the encode and match behaviors. When we use it, we leverage it to develop new functionalities for our app. Thus, this object is open for new development but closed for modification. We refrain from altering the object to align with our new developments.</p>
</li>
</ul>
<blockquote>
<p>One implementation (class, entity) should not be changed; it's open to being used for developing new functionalities but closed to modification. This is because altering it likely means changing its responsibility, thereby deviating from its original purpose. This could occur if you didn't initially choose the correct boundary for its single responsibility or if you're attempting to add additional responsibilities, which isn't ideal.</p>
</blockquote>
<h2 id="heading-dependency-inversion">Dependency Inversion</h2>
<p>Let's discuss Dependency Inversion first to better understand Liskov Substitution and Interface Segregation because they are related to abstraction.</p>
<ul>
<li><p>This principle is related to abstraction, stating that one implementation (object) should depend on the abstraction of another. By this way we are able to <strong>decouple</strong> the objects.</p>
</li>
<li><p>Consider this as when you have two objects, you shouldn't directly link them but instead link them through a contract(Interface).</p>
</li>
<li><p>This approach provides the possibility to change the implementation in the future. For example, we may find a better or more performant solution down the line, allowing us to replace the implementation without affecting those who are consuming it.</p>
</li>
</ul>
<h2 id="heading-liskov-substitution">Liskov Substitution</h2>
<ul>
<li><p>This principle tells us that when you use a contract, any implementation provided should not change the logic of your algorithm. It should not change the way your application is working.</p>
</li>
<li><p>Let's dive into a problem which it addresses.</p>
<ul>
<li><p>If you see the below example getSomeSet() follows the <strong>Dependency Inversion</strong> principle, but it <strong>violates LISKOV principle</strong>.</p>
<pre><code class="lang-JAVA">  <span class="hljs-comment">/*
  Liskov principle:
  It says not to use something more than the contract offers you

  Never do like this, it violates LISKOV principle
  */</span>
  <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example1</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Set&lt;Integer&gt; set = getSomeSet();
        <span class="hljs-comment">// here you can not consider that they are always sorted</span>
        <span class="hljs-comment">// someone may change the code instead of TreeSet, for ex: they may provide HashSet</span>
    }

    <span class="hljs-comment">/*
    assume this is in the other class
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Set&lt;Integer&gt; <span class="hljs-title">getSomeSet</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeSet&lt;&gt;(); <span class="hljs-comment">// sorted</span>

        <span class="hljs-comment">// return new HashSet&lt;&gt;(); // not sorted anymore but still the program will work </span>
                                 <span class="hljs-comment">// we will not get any compilation error, but it may change how our app behaves earlier</span>
    }

  }
</code></pre>
</li>
<li><p>let's consider below example, it follows LISKOV principle.</p>
<pre><code class="lang-JAVA">    <span class="hljs-comment">/*
    Liskov principle:
    It says not to use something more than the contract offers you.
    */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example2</span> </span>{

      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
          Set&lt;Integer&gt; set = getSomeSet();
          <span class="hljs-comment">// here you can not consider that they are always sorted</span>
      }

      <span class="hljs-comment">/*
      assume this is in the other class
       */</span>
      <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> SortedSet&lt;Integer&gt; <span class="hljs-title">getSomeSet</span><span class="hljs-params">()</span> </span>{
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeSet&lt;&gt;(); <span class="hljs-comment">// sorted</span>
      }

    }
</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="heading-interface-segregation">Interface Segregation</h2>
<ul>
<li><p>This principle, like Liskov Substitution, is about abstraction. It suggests that when you create a contract, you should define its boundaries so that each entity consumes only what it needs.</p>
</li>
<li><p>Consider the example of TreeSet class in Java, which implements NavigableSet, SortedSet, and Set. SortedSet, positioned above Set, adds the requirement for sorted behavior to the collection.</p>
<pre><code class="lang-JAVA">    <span class="hljs-comment">/*
    Liskov principle:
    It says not to use something more than the contract offers you.
    */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example</span> </span>{

      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
          Set&lt;Integer&gt; set = getSomeSet();
          <span class="hljs-comment">// here you can not consider that they are always sorted</span>
      }

      <span class="hljs-comment">/*
      assume this is in the other class
       */</span>
      <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> SortedSet&lt;Integer&gt; <span class="hljs-title">getSomeSet</span><span class="hljs-params">()</span> </span>{
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeSet&lt;&gt;(); <span class="hljs-comment">// sorted</span>
      }

    }
</code></pre>
</li>
<li><p>Set, SortedSet, and NavigableSet exemplify Interface Segregation. We shouldn't force someone to always use a sorted Set; sometimes, we simply need a collection that doesn't allow duplicates. Segregating the responsibility of the contract allows for flexibility in implementation choices.</p>
</li>
</ul>
<blockquote>
<p>Interface Segregation ensures that contracts define boundaries, allowing entities to consume only what they need, as seen in Java's TreeSet class implementing various set interfaces with distinct responsibilities.</p>
</blockquote>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, adhering to the SOLID principles—Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion—can greatly enhance the quality, maintainability, and flexibility of your software projects.</p>
]]></content:encoded></item></channel></rss>