Solid Principles

Can Avcı
4 min readFeb 25, 2022

Solid is a set of rules that guide us how to achive maintainable and clean code in object-oriented programming.
Following are the five concepts that constitute SOLID principles

1. Single Responsibility principle
2. Open/Closed principle
3. Liskov Substitution principle
4.Interface Segregation principle
5. Dependency Inversion principle

So lets dive into these concepts with example

Solid

this point out that a class should have single a responsibility. moreover , it should have reason to change . in other words a class should have solved one and only one problem . a simple example is given below.

class Page
{
protected $title;

public function getPage($title)
{
return $this->title;
}

public function formatJson()
{
return json_encode($this->getTitle());
}
}

this class seems like a good idea since it is responsible for it is own formatting
However if want to change output the JSON object or add another type of output to class ?
we would need to alter the class by adding another method or changing existing method to suit. as we add more field. the class will become more complex.

A better approach is that keep class as possible as simple . then create a class to be used to handle JSON formatting for the page.

class Page
{
protected $title;

public function getPage($title)
{
return $this->title;
}
}

class JsonPageFormatter
{
public function format(Page $page)
{
return json_encode($page->getTitle());
}
}

doing this separate Pager class from its formatting. if wanted to create xml format. we could create XmlPageFormatter .

Open/Closed

in this principle classes should be open for extension, but be closed for modification. in essence , this classes should be extended to change functionality , rather than being altered into something else.
lets take the following example into consideration

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

class Board
{
public $rectangles = [];

public function calculateArea()
{
$area = 0;
foreach ($this->rectangles as $rectangle) {
$area += $rectangle->width * $rectangle->height;
}
return $area;
}
}

We have Rectangle class that contains data, and a Board class is used to find out area of Rectangle collections by looping through the items in the $rectangles property . The problem with this setup is that we are restricted by the types of object that we can pass to Board. what if we wanted to pass a Circle class to the Board. we would need to write conditional statements
( if ($rectangle instanceof Circle) ) which we would violate open/close principle. the correct approach to this issue is to create Shape Interface and move the calculation of code from calculateArea into the class which implements Shape.

interface Shape {
public function area();
}

class Rectangle implements Shape {
public function area() {
return $this->width * $this->height;
}
}

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;
}
}

Thus Board class can now be reworked so that it doesn’t care what type of shape is passed into . on condition that they implement the area method

Liskov Substitution Principle

this principle emphasize that object should be replaceable by their subtypes without altering how the program works.
which means derived class must be substitutable for their base classes without causing an error.

The code below define a Rectangle class that we can use to create and calculate the area of a rectangle.

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

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

public function getArea()
{
return $this->height * $this->width;
}
}

Using that we can extend this into Square class . Because these both are the same calculation pattern. Square exceptionally have the same width and height fields in size. so we are supposed to some arrengement on it.

class Square extends Rectangle
{
public function setWidth($w)
{
$this->width = $w;
$this->height = $w;
}

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

That seems fine. but ultimately a square isnot a rectangle. overriding lots of code in classess to suit. the more code you add to override the present conditions the more fragile your code will become.

one solution to rectangle ans square is to define an interface called Quadrilateral (naming is totally up to you. i choosed this for convenience) implement this for both Rectangle and Square.
Thus we will have allowed class to responsible for their own data.and enforcing to define methods which was declared in interface.

interface Quadrilateral {
public function setHeight($h);

public function setWidth($w);

public function getArea();
}

class Rectangle implements Quadrilateral{
public function setHeight($h)
{
// TODO: Implement setHeight() method.
}

public function setWidth($w)
{
// TODO: Implement setWidth() method.
}

public function getArea()
{
// TODO: Implement getArea() method.
}

}

class Square implements Quadrilateral{
public function setHeight($h)
{
// TODO: Implement setHeight() method.
}

public function setWidth($w)
{
// TODO: Implement setWidth() method.
}

public function getArea()
{
// TODO: Implement getArea() method.
}
}

the bottom line here is that for your project is that . if you find yourself overriding many codes. you architecture may be wrong and you should think about liskov principle.

--

--