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
SOLID stands for..
S.O.L.I.D is an acronym for the first five object-oriented design(OOD)** principles** by Robert C. Martin.
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());
}
}
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());
}
}
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;
}
}
}
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();
}
}
class Board {
public $shapes;
public function calculateArea() {
$area = 0;
foreach ($this->shapes as $shape) {
$area+= $shape->area();
}
return $area;
}
}
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
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.
***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;
}
}
class Square extends Rectangle {
public function set($w) {
$this->width = $w;
$this->height = $w;
}
public function setH=($h) {
$this->height = $h;
$this->width = $h;
}
}
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 ;
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()
}
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{
}
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;
}
}
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;
}
}
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!!
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)
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.
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
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
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 !
SRP:
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
Hi there, you can check out my linked in by clicking the icon in my profile!
Very good write up on design principles, here are five core object-oriented principles SOLID, xalitech.com/solid-principles-of-o...
Fantastic write-up! only that in maths a square is a subset of a rectangle.
dev.to/evrtrabajo/solid-in-php-d8e
So everything here is about to always construct classes for only one purpose and functionality.
Cool article!