SOLID design principle

SOLID is an acronym. which full meaning is

S – Single-responsibility principle
O – Open-closed principle
L – Liskov substitution principle
I – Interface segregation principle
D – Dependency Inversion Principle

When a developer develop a software if he flows this principle it is easy to maintenance and reuse and test his module. let see what is S.O.L.I.D is.

Understanding “S”- SRP (Single responsibility principle)

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

For example Student class represent student

Understanding “O” – Open closed principle

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

Understanding “L”- LSP (Liskov substitution principle)

Every subclass/derived class should be substitutable for their base/parent class.

Understanding “I” – ISP (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.

Understanding “D”- 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.

Code to interface not in implementation

This principle is really about dependency relationships which have to be carefully managed in a large app. – Erich Gamma

The interface represents “what” the class can do but not “how” it will do it, which is the actual implementation.

When you program to interface not implementation your code become more decouple and easy to maintenance.

For example….

 
class Mysql
{
    protected $db = null;

    public function connect($dsn, $user = '', $pass = '')
    { 
        $this->db = new PDO($dsn, $user, $pass);
    }

    public function query($query)
    {
        return $this->db->query($query);
    }
}

Here Mysql fully depend on PDO class. MySql tightly couple with PDO and it is hard to test with unit testing. Now use this class to interact with other class

 
class Book {

    private $database = null;

    public function __construct() {
        $this->database = new Mysql();
        $this->database->connect('mysql:host=localhost;dbname=oop', 'root', '');
    }

    public function getAll()
    {
       $books = $this->database->query('SELECT * FROM books ORDER BY id DESC');
       print_r($books);
    }
}

$user = new Book();
$user->getAll();

This books class also have some porblem

  • Book class fully dependent with Mysql class and it is hard to unit test.
  • If you need another model or class you have to write this mysql connection code again which is against the law DRY(dont repeat your self).
  • If you need another database or change database credential then you have to modify all class which is difficult to maintenance.
  • There is no need to know about database connection and query related work by Book class which is against (SOLID) single responsibility principle.
    So what is the solution

Solution is dependency injection or inversion which is SOLID D. Find out the dependency of the class and inject those dependency through method.

 
class Book {

    private $database = null;

    public function __construct(Mysql $database) {
        $this->database = $database;
    }

    public function getAll()
    {
        $books = $this->database->query('SELECT * FROM books ORDER BY id DESC');
        print_r($books);
    }
}



$database = new Mysql();
$database->connect('mysql:host=localhost;dbname=oop', 'root', '');
$user = new User($database);
$user->getUsers();

After refactor the class now we can easily inject the database connection and book class does not worry about the database connection related work so it is fully single responsible. And easily test this class. But there is a problem and it is Book class only work with MySql Class.Solve this problem we can use Dependency inversion principle

 
interface DB
{
    public function connect($dsn, $user = '', $pass = '');
    public function query($query);
}

class Mysql implement DB	
{
    protected $db = null;

    public function connect($dsn, $user = '', $pass = '')
    { 
        $this->db = new PDO($dsn, $user, $pass);
    }

    public function query($query)
    {
        return $this->db->query($query);
    }
}

we can also write others database connection class which also implement DB interface.

 

class Sqlite implements DB
{
    protected $db = null;

    public function connect($dsn, $user = '', $pass = '')
    {
        $this->db = new PDO($dsn);
    }

    public function query($query)
    {
        return $this->db->query($query);
    }
}

Here come our main focus that is code to interface not to implementation

 


class Book
{

    private $database = null;

    public function __construct(DB $database)
    {
        $this->database = $database;
    }

    public function getAll()
    {
        $books = $this->database->query('SELECT * FROM books ORDER BY id DESC');
        print_r($books);
    }
}


$database = new Mysql();
$database->connect('mysql:host=localhost;dbname=oop', 'root', '');
$book = new Book($database);
$book->getAll();

Now we can easily inject any database connection and query related class which implement DB interface.