DEV Community

Cover image for SOLID PRINCIPLES: To start with Object-oriented programming
Emma Ham
Emma Ham

Posted on • Edited on

SOLID PRINCIPLES: To start with Object-oriented programming

Hi everyone, today I would like to discuss about something solid.
solid? Of course, no! Not that solid.
wait... ah maybe exactly that solid!

I went to an interview today and was asked this question about "Solid principles". And I was like "um... not really sure" which means "I don't know what you are talking about".
On my way out of the room, I felt like I was definitely in a need of researching about solid principles to properly start off going further to object-oriented programming world in the right direction.

So today, I would like to cover this topic with a few basic definitions and easy examples to help ourselves out to understand the concept in general.

1) What and why : SOLID PRINCIPLES

Alt Text

SOLID stands for..

S.O.L.I.D is an acronym for the first five object-oriented design(OOD)** principles** by Robert C. Martin.

Alt Text

It is just really about a set of principles that we can keep in mind in terms of coding, refining and structuring code.

SOLID does..

By reading it, I had no idea what it is supposed to mean,by all mean. As far as understand, These principles are all about making it easier and cleaner for developers to develop software which are easy to maintain and modify when the scale of it is going bigger and get more complicated. So we can really understand this concept as a way of approaching object-oriented programming by cutting down the unnecessary part and organize the code in a way of refining and re-structuring.

2) Dive into : SOLID PRINCIPLES

First Principle: Single-responsibility Principle

"A class should have one and only one reason to change, meaning that a class should have only one job."

It is as simple as it sounds which means that a class should only perform a single job not the multiple jobs in a same class.
Let's have a look at the example down.

class Book {
  protected $Author;

  public getAuthor($Author) {
    return $this->Author;
  }

  public function formatJson() {
    return json_encode($this->getAuthor());
  }
}
Enter fullscreen mode Exit fullscreen mode

What do you think, do you think you are getting a hang of it?
We can clearly see that Class named 'Book' is performing more than one job which are getting the author of a book and also it is encoding the output to Json format. But what if we want to have an output in a different format not only Json? In that case, we probably have to write few more lines to add more method for that or modifying the existing code. It could look like a minor work at the moment because this example we have right here is extremely simple but imagine there are hundreds or even thousands of classes, it would be better off preventing confusions in advance by implementing these principles.

So, the approach that we can take would be something like

class Book {
  protected $Author;

  public getAuthor($Author){
    return $this->Author;
  }
}

class JsonBookForm {
    public function format(Book $Author) {
        return json_encode($Author->getAuthor());
    }
}
Enter fullscreen mode Exit fullscreen mode

By doing that, If we want to get a different type of output then we don't need to make a change in the Book class directly.

Second Principle: Open-closed Principle

"Objects or entities should be open for extension, but closed for modification."

I know it is getting boring, but this is actually meaning that classes should be extended to change functionality, rather than being modified. It should be a way of extending its functionality when they want to add more options and it should not be the case just changing the existing code directly.

There is a common example which is like down below.

class Triangle{
  public $width;
  public $height;
}

class Board {
  public $triangles= [];
  public function calculateArea() {
    $area = 0;
    foreach ($this->triangles as $triangle) {
      $area += $triangle->width * $triangle->height* 1/2;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

We have a Triangle class that contains the data for a Triangle which are width and height, and a Board class that is used as a array of Triangle objects.
This code at the moment looks absolute fine but when you think about shape and calculating area function, maybe you might want to use this function later for other shapes as well not only just triangle to increase the efficiency. At the moment, these lines of codes are not allowing this by limiting the Board class only working with the Triangle class.

The approach we can try here is

interface Shape {
   public function area();
}

class Triangle implements Shape {
  public function area() {
    return $this->width * $this->height *1/2;
  }
}
class Circle implements Shape {
  public function area() {
    return $this->radius * $this->radius * pi();
  }
}
Enter fullscreen mode Exit fullscreen mode
class Board {
  public $shapes;

  public function calculateArea() {
    $area = 0;
    foreach ($this->shapes as $shape) {
      $area+= $shape->area();
    }
    return $area;
  }
}
Enter fullscreen mode Exit fullscreen mode

In this way, we don't need to specify the name of the class in the class name Board, so whenever we want to add more properties something like Rectangle we can easily add a class named itself and implements Shape interface so that we can use area() in a Board class.

Third Principle: Liskov substitution principle

"Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T."

Okay, I know it does sound like my math teacher back in high school. can't really follow.
But look at this picture here
Alt Text
Does this look familiar? We have probably seen this picture in school and I am sure your math teacher must have mentioned it. Where this graph can be implied in our world is something related to the previous example. Shapes.

Speaking of shapes, there are a lot of shapes, right? There are circles, and triangles and rectangles and square.. wait, we all know that rectangle and square are similar but not same right?
Rectangle includes square in terms of the fact that rectangle needs more conditions to be square. So it would be like this.

Alt Text
***oops I put the name the other way around here!!!
The common code we can do here would be

class Rectangle {
  public function setW($w) { 
      $this->width = $w;
  }

  public function setH($h) {
      $this->height = $h;
  }

  public function Area() {
      return $this->height * $this->width;
  }
}
Enter fullscreen mode Exit fullscreen mode
class Square extends Rectangle {
  public function set($w) {
    $this->width = $w;
    $this->height = $w;
  }

  public function setH=($h) {
    $this->height = $h;
    $this->width = $h;
  }
}
Enter fullscreen mode Exit fullscreen mode

But did you realize that there are multiple lines looking exactly same? and considering the fact that in this tutorial, we are doing our best to make the code maintainable and efficient, this way should be avoided and rather trying this way.

interface Setshape {
  public function setH($h);

  public function setW($w);

  public function Area();
}

class Rectangle implements Setshape ;

class Square implements Setshape ;
Enter fullscreen mode Exit fullscreen mode

By doing this way, we don't have to write the same code over and over again, and we can just simple implements the interface to the class.

Forth Principle: Interface segregation principle

"A client should never be forced to implement an interface that it doesn't use or clients shouldn't be forced to depend on methods they do not use."

We are almost there, the forth principle means that classes should not be forced to implement interfaces they do not use. For example, let's say there are some classes looking like

interface Employee {

  public function generatereport()

  public function clockin()

  public function clockout()

  public function customerservice()

  public function getPaid()

}
Enter fullscreen mode Exit fullscreen mode

And let's say there is a duty manager who is doing the most of the functions above but not clockin and clockout because the could possibly get salary not hourly so for them those two functions will be never used. To follow the principle here once again, we should approach it this way.

interface Generalemployee{
 public function clockin()
 public function clockout()
}

interface Employee{
 public function customerservice()
 public function getPaid()
}

interface management{
  public function generatereport()
}

class Staff implements Generalemployee, Employee{
}

class Manager implements Employee, management{
}
Enter fullscreen mode Exit fullscreen mode

Last Principle: Dependency Inversion principle

"Entities must depend on abstractions not on concretions. It states that the high level module must not depend on the low level module, but they should depend on abstractions."

I will go straight to the example for this one

class Getuserlists {
    private $dbConn;

    public function __construct(MySQLConn, $dbConn) {
        $this->dbConn= $dbConn;
    }
}
Enter fullscreen mode Exit fullscreen mode

It is common mistake because according to the hierarchy structure, Getuserlists class is higher module and MySQLConn is a lower module, however, you can easily notice that the class is depending on MySQLConn thoroughly and it would confuse later if we want to use other database not just MySQL.

So the solution on this would be...

interface DbConnectionInterface {
    public function connect();
} 

class MySqlConn implements DbConnectionInterface {
    public function connect() {}
}

class Getuserlists {
    private $dbConn;
    public function __construct(DbConnectionInterface $dbConn) {
        $this->dbConn= $dbConn;
    }
}
Enter fullscreen mode Exit fullscreen mode

According to the little snippet above, you can now see that both the high level and low level modules depend on abstraction.

Congratulations!! We took a step towards Object-oriented programming world!!

Alt Text

Conclusion

SOLID principles seemed to be a very confusing at first, and still will have some more struggles in order to make it to mine and get to the point where I can use comfortably without even realizing but It is always good to have some guidelines we can follow, isn't it?

Top comments (11)

Collapse
 
pedro00dk profile image
Pedro Henrique

Cool article, I didn't remember these definitions anymore, even though I use then when codding.
Just a detail, the square-rectangle diagram should be the opposite, as squares have more constraints, so they are a subset of rectangles.

Collapse
 
davjvo profile image
Davmi Jose Valdez Ogando

SOLID can be tricky to remember every single component, or at least for me.

Like the simple explanation of this post, although when that question comes simply say yes, modern common architectures all implement SOLID principle, so if you implement any architecture step by step you are for sure following SOLID principle

Collapse
 
corsari profile image
corsari • Edited

Hello Emma, thank you for this synthesis and summary altogether.

Do you kindly have a link to VERY BASIC PHP OOP tutorial ,

I mean a MINIMAL app, like a TODO list or similar, where the TUTOR applies the OOP and its S.O.L.I.D. principles so much. I mean such a kind of over-use of classes as a stretch,

as the word say ---> for tutoring the student

also (and mainly) to show how classes passes tasks one to each other

Unfortunately if you google for PHP OOP, you are flooded with thousands of guides and tutorials that are really silly

They teaches you the dictionary of PHP classes and their grammatical and magic methods ... but NO a SINGLE ONE of them shows a practical implementation or (and MAINLY) the interaction between the classes.

OOP is abstraction and tutorials that add abstraction to abstraction ... are really worse...

... the best idea would be a tutorial where the supposed above TODO list or basic app, as in your examples in this great article, is FIRST written in the classical old style procedural way

then the SAME identical is realized with as many classes as possible (either "excessive" use of classes, the idea there is to show how to make them interact with each other the proper way)

Thank you for any link or , even more, if you may think to write one :-) since I like so much your way to explain ;_)

P.S. I suppose it could become really referenced and popular

Collapse
 
jcmarquet profile image
Jean-Christophe MARQUET

I spend so much time talking about SOLID principles to junior members of the team but I don't seem to be able to remember what principle is what letter. It's kind of ridiculous really.

This article is now in my favorites and I intend to use it when explaining the gains of these principles. It's very simple and I like the before/after approach. Good job and thanx !

Collapse
 
vlasales profile image
Vlastimil Pospichal

SRP:

<?php declare(strict_types=1);

class Book implements JsonSerializable {
    private $author;

    function __construct(string $author) {
        $this->author = $author;
    }

    public function getAuthor() {
        return $this->author;
    }

    public function jsonSerialize() {
        return $this->getAuthor();
    }

}

$book = new Book('Author');
echo json_encode($book);
Collapse
 
bilalisz profile image
Muhammad Bilal

Hi dear,,,
You know, this is too useful for me I'm a fresh graduated and now turn for developing, but a lot of problems i face, can you helf me for more like that's concept for development,would you give me your social media address

Collapse
 
ham8821 profile image
Emma Ham

Hi there, you can check out my linked in by clicking the icon in my profile!

Collapse
 
xalitech profile image
Xali Tech

Very good write up on design principles, here are five core object-oriented principles SOLID, xalitech.com/solid-principles-of-o...

Collapse
 
kehinde_issa profile image
Issa kehinde

Fantastic write-up! only that in maths a square is a subset of a rectangle.

Collapse
 
evrtrabajo profile image
Emmanuel Valverde Ramos
Collapse
 
rohullahayobi profile image
R.A.

So everything here is about to always construct classes for only one purpose and functionality.
Cool article!