Slide 1

Slide 1 text

SOLID

Slide 2

Slide 2 text

WHAT IS SOLID? A set of principles specifically designed by the great uncle Bob to ensure that an application is maintainable, extensible, and easy to understand.

Slide 3

Slide 3 text

THE PRINCIPLES ▸ Single Responsibility ▸ Open Closed ▸ Liskov Substitution ▸ Interface Segregation ▸ Dependency Inversion

Slide 4

Slide 4 text

THE SINGLE RESPONSIBILITY PRINCIPLE All classes should have only 1 responsibility. No need to go hardcore purist with this though. An example of a class that still follows the single responsibility principle is an Auth class that handles things such as user login and registration.

Slide 5

Slide 5 text

THE OPEN CLOSED PRINCIPLE Your code should be open to extension, but closed to modification.

Slide 6

Slide 6 text

EXAMPLE OF VIOLATION function subtractDiscount($price, $months) { if ($months == 1) { $discount = 0; } else if ($months == 6) { $discount = $price * 0.15; } else if ($months == 12) { $discount = $price * 0.25; } return $price - $discount; }

Slide 7

Slide 7 text

HOW TO FIX? Separate the extensible behaviour behind an interface and flip the dependencies.

Slide 8

Slide 8 text

THE INTERFACE interface DiscountInterface { public function getDiscountedAmount($amount); }

Slide 9

Slide 9 text

THE CLASS class AnnualDiscount implements DiscountInterface { const DISCOUNT_PERCENTAGE = 0.25; public function getDiscountedAmount($amount) { return $amount - ($amount * self::DISCOUNT_PERCENTAGE); } }

Slide 10

Slide 10 text

USING IT function subtractDiscount($price, DiscountInterface $discount) { return $discount->getDiscountedAmount($price); }

Slide 11

Slide 11 text

THE LISKOV SUBSTITUTION PRINCIPLE If a module is using a base class then that reference to the base class should be able to be replaced by a derived class without affecting any of the existing functionality.

Slide 12

Slide 12 text

EXAMPLE OF VIOLATION: PART 1 class Rectangle { protected $width; protected $height; public function setWidth($width) { $this->width = $width; } ... }

Slide 13

Slide 13 text

EXAMPLE OF VIOLATION: PART 2 class Square extends Rectangle { public function setWidth($width) { $this->width = $width; $this->height = $width; } }

Slide 14

Slide 14 text

HOW TO FIX? To fix any classes that violate this principle find the base functionality present in all the classes, separate this in a new base class and use that instead.

Slide 15

Slide 15 text

THE INTERFACE SEGREGATION PRINCIPLE A class should not be forced to implement methods that it does not use. Basically, your interfaces should not force additional and unnecessary methods on classes.

Slide 16

Slide 16 text

HOW TO FIX This does take some planning and additional thought. You will have to separate your interfaces so that only the required methods are present. A class CAN implement multiple interfaces.

Slide 17

Slide 17 text

THE DEPENDENCY INVERSION PRINCIPLE A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend upon details. Details should depend upon abstractions.

Slide 18

Slide 18 text

WHY? Changes are risky. Depending on a concept is a lot safer than depending on an implementation.

Slide 19

Slide 19 text

SCENARIO Logger class responsible of logging various bits of data. The original implementation was written in such a way that the logs are written to file. All of a sudden you change your mind and you want to log to a DB instead.

Slide 20

Slide 20 text

HOW TO FIX? Ye olde Gateway Pattern to the rescue!!!

Slide 21

Slide 21 text

CODE EXAMPLE: THE INTERFACE interface DataStoreInterface { public function log($data); }

Slide 22

Slide 22 text

CODE EXAMPLE: THE CLASS class FileStore implements DataStoreInterface { public function log($data) { return $this->write($data); } private function write($data) { try { return file_put_contents('log.txt', $data, FILE_APPEND); } catch (Exception $e) { return false; } } }

Slide 23

Slide 23 text

CODE EXAMPLE: THE GATEWAY class Logger { private $store; public function __construct(DateStoreInterface $store) { $this->store = $store; } public function log($data) { return $this->store->log($data); } }

Slide 24

Slide 24 text

CODE EXAMPLE: USAGE $logger = new Logger(new FileStore()); $logger->log('Hello'); If we eventually implement a DB storage class: $logger = new Logger(new DBStore()); $logger->log('Hello DB');