Categories
OOP

The SOLID principles of object-oriented design

SOLID is an acronym representing a set of five design principles to make software design more understandable, flexible, and maintainable. These principles were introduced by Robert C. Martin and are widely used in object-oriented programming. Let’s dive into these principles with explanations and examples using PHP:

1. Single Responsibility Principle (SRP)

  • Definition: A class should have only one reason to change, meaning it should have only one job or responsibility.
  • Concept: Encourage creating classes that have only one functionality.
<span class="hljs-comment">// Violating SRP</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserManager</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// code to create a user</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logError</span><span class="hljs-params">($error)</span> </span>{
        <span class="hljs-comment">// code to log error</span>
    }
}

<span class="hljs-comment">// Complying with SRP</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserManager</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// code to create a user</span>
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Logger</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logError</span><span class="hljs-params">($error)</span> </span>{
        <span class="hljs-comment">// code to log error</span>
    }
}

2. Open/Closed Principle (OCP)

  • Definition: Software entities should be open for extension but closed for modification.
  • Concept: You should be able to add new functionality without changing existing code.
<span class="hljs-comment">// Violating OCP</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rectangle</span> </span>{
    <span class="hljs-keyword">public</span> $width;
    <span class="hljs-keyword">public</span> $height;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AreaCalculator</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculate</span><span class="hljs-params">($rectangle)</span> </span>{
        <span class="hljs-keyword">return</span> $rectangle->width * $rectangle->height;
    }
}

<span class="hljs-comment">// Complying with OCP</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">area</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rectangle</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-keyword">public</span> $width;
    <span class="hljs-keyword">public</span> $height;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">$this</span>->width * <span class="hljs-keyword">$this</span>->height;
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-keyword">public</span> $radius;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> pi() * pow(<span class="hljs-keyword">$this</span>->radius, <span class="hljs-number">2</span>);
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AreaCalculator</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculate</span><span class="hljs-params">(Shape $shape)</span> </span>{
        <span class="hljs-keyword">return</span> $shape->area();
    }
}

3. Liskov Substitution Principle (LSP)

  • Definition: Subtypes must be substitutable for their base types without altering the correctness of the program.
  • Concept: Objects of a superclass should be replaceable with objects of a subclass without affecting the program’s correctness.
<span class="hljs-comment">// Violating LSP</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bird</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fly</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// code for flying</span>
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Penguin</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Bird</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fly</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Penguins can’t fly!</span>
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">Exception</span>(<span class="hljs-string">'Can’t fly'</span>);
    }
}

<span class="hljs-comment">// Complying with LSP</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Bird</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">move</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Sparrow</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Bird</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">move</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// code for flying</span>
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Penguin</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Bird</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">move</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// code for walking</span>
    }
}

4. Interface Segregation Principle (ISP)

  • Definition: Clients should not be forced to implement interfaces they do not use.
  • Concept: Instead of having a bloated interface, split it into smaller and more specific ones so that the clients will only have to know about the methods that are of interest to them.
<span class="hljs-comment">// Violating ISP</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Worker</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span>;
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">eat</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HumanWorker</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Worker</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// working</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">eat</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// eating</span>
    }
}

<span class="hljs-comment">// Complying with ISP</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Workable</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Eatable</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">eat</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HumanWorker</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Workable</span>, <span class="hljs-title">Eatable</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// working</span>
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">eat</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// eating</span>
    }
}

5. Dependency Inversion Principle (DIP)

  • Definition: Depend on abstractions, not on concretions.
  • Concept: High-level modules should not depend on low-level modules but should depend on abstractions. Also, abstractions should not depend on details. Details should depend on abstractions.
<span class="hljs-comment">// Violating DIP</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LightBulb</span> </span>{}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Switch</span> </span>{
    <span class="hljs-keyword">private</span> $bulb;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">operate</span><span class="hljs-params">(LightBulb $bulb)</span> </span>{
        <span class="hljs-keyword">$this</span>->bulb = $bulb;
        <span class="hljs-comment">// operate the light bulb</span>
    }
}

<span class="hljs-comment">// Complying with DIP</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Switchable</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">operate</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LightBulb</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Switchable</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">operate</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// turn on/off the light bulb</span>
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Switch</span> </span>{
    <span class="hljs-keyword">private</span> $device;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">operate</span><span class="hljs-params">(Switchable $device)</span> </span>{
        <span class="hljs-keyword">$this</span>->device = $device;
        <span class="hljs-keyword">$this</span>->device->operate();
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *