REVISITING SOLID PRINCIPLE
at “Eskimi Tech Adda”
WHO AM I?
Anis Uddin Ahmad
Head of Software Development
Devnet Limited.
OBJECT
ORIENTED
DESIGN
is the point of discussion
LET’S START WITH SOME PREREQUISITES
What is the difference between
Abstract Class and Interface
CLASSES - BUILDING BLOCK OF OOP
➤ Defines and classifies Object types
CLASSES - BUILDING BLOCK OF OOP
➤ Produce milk
➤ Have hair*
➤ Give live birth
➤ Two legs
➤ Have feather
➤ Lay eggs
walk()
giveBirth()
fly()
layEggs()
➤ Have Backbone
➤ Ability of reproduction
INTERFACE - DEFINE CONTRACTS
➤ Defines a set of abilities
➤ Declares the operations that can be invoked on an object
SOLID - BASIC PRINCIPLES OF OOD
SINGLE RESPONSIBILITY PRINCIPLE
A class should have
one, and only one,
reason to change
Responsibility == Reason to Change
— Uncle Bob
SRP EXAMPLE - VIOLATED (1)
class SalesReport
{
private string $title;
private array $conditions;
private array $connectionParams;
public function __construct(string $title, array $conditions, array $connectionParams)
{
$this->title = $title;
$this->conditions = $conditions;
$this->connectionParams = $connectionParams;
}
public function getData()
{
return [
'title' => $this->getTitle(),
'generatedOn' => new DateTime(),
'date' => $this->loadReportData($this->conditions, $this->connectionParams),
];
}
// Cont…
SRP EXAMPLE - VIOLATED (2)
public function getData()
{
return [
'title' => $this->getTitle(),
'generatedOn' => new DateTime(),
'date' => $this->loadReportData($this->conditions, $this->connectionParams),
];
}
public function getJsonData()
{
return json_encode($this->getData());
}
private function loadReportData(array $conditions, array $connectionParams) : array
{
// Create PDO Connection
// Query for Report Data based on conditions
// return Array result
}
// Getter/Setter and other functions
}
SRP EXAMPLE - REFACTORED (1)
// ———— Data Accessor —————
class DataRepository {
public function __construct(array $connectionParams) { /** **/}
public function findByID(int $id) { /** **/}
public function findByCondition(array $conditions): array { /** **/}
private function connectDataSource() { /** **/}
}
class SalesDataRepository extends DataRepository {/** **/}
SRP EXAMPLE - REFACTORED (2)
// ———— Data Accessor —————
class DataRepository {
public function __construct(array $connectionParams) { /** **/}
public function findByID(int $id) { /** **/}
public function findByCondition(array $conditions): array { /** **/}
private function connectDataSource() { /** **/}
}
class SalesDataRepository extends DataRepository {/** **/}
// ———— Content Formatting —————
interface Exportable {
public function getData();
}
class JSONReportExporter {
public function export(Exportable $report)
{
return json_encode($report->getData());
}
}
SRP EXAMPLE - REFACTORED (3)
class SalesReport implements Exportable
{
private string $title;
private array $conditions;
private DataRepository $repository;
public function __construct(string $title, array $conditions, DataRepository $repository)
{
$this->title = $title;
$this->conditions = $conditions;
$this->repository = $repository;
}
public function getData()
{
return [
'title' => $this->getTitle(),
'generatedOn' => new DateTime(),
'data' => $this->repository->findByCondition($this->conditions),
];
}
// Removed getJsonData() and loadReportData()
// Other relevant methods
}
SRP EXAMPLE - REFACTORED (4)
$dataSource = new SalesDataRepository($connectionParams);
$conditions = [
'from' => $startDate,
'to' => $endDate,
];
$salesReport = new SalesReport("Summary of January 2020", $conditions, $dataRepository);
$formatter = new JSONReportExporter();
$formatter->export($salesReport);
➤ Cleaner Code
➤ Easier to understand
➤ Easier to change code
➤ Code becomes reusable
➤ Easier to write test codes
SRP EXAMPLE - REFACTORED (4)
$dataSource = new SalesDataRepository($connectionParams);
$conditions = [
'from' => $startDate,
'to' => $endDate,
];
$salesReport = new SalesReport("Summary of January 2020", $conditions, $dataRepository);
$formatter = new JSONReportExporter();
$formatter->export($salesReport);
➤ Cleaner Code
➤ Easier to understand
➤ Easier to change code
➤ Code becomes reusable
➤ Easier to write test codes
➤ New features are easy to implement
class JSONReportExporter {
public function export(Exportable $report)
{
return json_encode($report->getData());
}
}
class CSVReportExporter {} { /** collapsed **/}
class XMLReportExporter {} { /** collapsed **/}
class PDFReportExporter {} { /** collapsed **/}
OPEN CLOSED PRINCIPLE
A class should be open
for extension, but
closed for
modification
You should be able to extend a classes
behaviour, without modifying it.
OCP EXAMPLE - VIOLATED (1)
class Rectangle {
private $width;
private $height;
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}
}
class Square {
private $length;
public function __construct($length)
{
$this->length = $length;
}
}
OCP EXAMPLE - VIOLATED (1)
class AreaCalculator {
protected $shapes;
public function __construct($shapes = array())
{
$this->shapes = $shapes;
}
public function sum()
{
foreach($this->shapes as $shape) {
if($shape instanceof Square) {
$area[] = pow($shape->length, 2);
} else if($shape instanceof Rectangle) {
$area[] = $shape->width * $shape->height;
}
}
return array_sum($area);
}
}
OCP EXAMPLE - VIOLATED (1)
class AreaCalculator {
protected $shapes;
public function __construct($shapes = array())
{
$this->shapes = $shapes;
}
public function sum()
{
foreach($this->shapes as $shape) {
if($shape instanceof Square) {
$area[] = pow($shape->length, 2);
} else if($shape instanceof Rectangle) {
$area[] = $shape->width * $shape->height;
}
}
return array_sum($area);
}
}
$shapes = array(
new Square(5),
new Square(2),
new Rectangle(6, 5)
);
$calc = new AreaCalculator($shapes);
echo $calc->sum();
OCP EXAMPLE - REFACTORED (1)
interface Shape {
public function area();
}
class AreaCalculator {
protected $shapes;
public function __construct($shapes = array())
{
$this->shapes = $shapes;
}
public function sum()
{
foreach($this->shapes as $shape) {
$area[] = $shape->area();
}
return array_sum($area);
}
}
OCP EXAMPLE - REFACTORED (1)
class Rectangle implements Shape {
private $width;
private $height;
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}
public function area() {
return $this->width * $this->height;
}
}
class Square implements Shape {
private $length;
public function __construct($length)
{
$this->length = $length;
}
public function area()
{
return pow($this->length, 2);
}
}
class Rectangle implements Shape {
private $width;
private $height;
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}
public function area() {
return $this->width * $this->height;
}
}
class Square implements Shape {
private $length;
public function __construct($length)
{
$this->length = $length;
}
public function area()
{
return pow($this->length, 2);
}
}
OCP EXAMPLE - REFACTORED (1)
class Circle implements Shape {
private $radius;
public function __construct($radius) {
$this->radius = $radius;
}
public function area() {
return pi() * pow($this->radius, 2);
}
}
OCP EXAMPLE - REFACTORED (1)
interface Shape {
public function area();
}
class AreaCalculator {
protected $shapes;
public function __construct($shapes = array()) {
$this->shapes = $shapes;
}
public function sum() {
foreach($this->shapes as $shape) {
$area[] = $shape->area();
}
return array_sum($area);
}
}
$shapes = array(
new Square(5),
new Square(2),
new Rectangle(6, 5),
new Circle(7),
);
$calc = new AreaCalculator($shapes);
echo $calc->sum();
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
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
Objects MUST be
replaceable by
instances of their
subtypes without
altering the correct
functioning of our
system.
LSP - VIOLATED (1)
abstract class LogWriter
{
abstract function write($message);
}
class FileWriter extends LogWriter {
function write($message){ /** Write to file **/}
}
class EmailWriter extends LogWriter {
function write($message)
{
$this->sendEmail($message, $this->getReceivers());
}
private function sendEmail($content, array $to) {/** **/}
}
class SlackWriter extends LogWriter { /** **/}
class DBWriter extends LogWriter { /** **/}
LSP - VIOLATED (2)
class Logger {
private $writers;
public function addWriter(LogWriter $writer)
{
$this->writers[] = $writer;
}
public function log($message)
{
foreach ($this->writers as $logWriter) {
$logWriter->write($message);
}
}
}
$logger = new Logger();
$logger->addWriter(new FileWriter());
$logger->addWriter(new DBWriter());
$logger->addWriter(new EmailWriter());
$logger->log("We are at Eskimi Tech Adda!");
LSP - VIOLATED (3)
class UserRegistration extends EmailWriter {
function sendConfirmationEmail($message)
{
$mailBody = $this->loadTemplate('confirmation.tmpl', $this->getUser());
$this->write($mailBody);
}
// Overwriting to work with registering user instead of logging address
function write($message)
{
$this->sendEmail($message, $this->getReceivers());
$this->sendEmail($message, $this->getUser()->getEmail());
}
}
LSP - VIOLATED (4)
$logger = new Logger();
$logger->addWriter(new FileWriter());
$logger->addWriter(new DBWriter());
$logger->addWriter(new UserRegistration());
$logger->log("We are at Eskimi Tech Adda!");
class UserRegistration extends EmailWriter {
function sendConfirmationEmail($message)
{
$mailBody = $this->loadTemplate('confirmation.tmpl', $this->getUser());
$this->write($mailBody);
}
// Overwriting to work with registering user instead of logging address
function write($message)
{
$this->sendEmail($message, $this->getReceivers());
$this->sendEmail($message, $this->getUser()->getEmail());
}
}
LSP - VIOLATED (5)
$logger = new Logger();
$logger->addWriter(new FileWriter());
$logger->addWriter(new DBWriter());
>> $logger->addWriter(new EmailWriter()); // <— Unexpected behaviour
$logger->log("We are at Eskimi Tech Adda!");
class UserRegistration extends EmailWriter {
function sendConfirmationEmail($message)
{
$mailBody = $this->loadTemplate('confirmation.tmpl', $this->getUser());
$this->write($mailBody);
}
// Overwriting to work with registering user instead of logging address
function write($message)
{
$this->sendEmail($message, $this->getReceivers());
$this->sendEmail($message, $this->getUser()->getEmail());
}
}
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
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
Develop small,
specific interfaces
instead of big,
general-purpose
interfaces
ISP - VIOLATED (1)
interface SoftwareGuy
{
public function code();
public function test();
}
class ProjectManagement
{
public function implement(SoftwareGuy $member)
{
$member->code();
}
public function test(SoftwareGuy $member)
{
$member->test();
}
}
ISP - VIOLATED (2)
interface SoftwareGuy
{
public function code();
public function test();
}
class ProjectManagement
{
public function implement(SoftwareGuy $member)
{
$member->code();
}
public function test(SoftwareGuy $member)
{
$member->test();
}
}
class Programmer implements SoftwareGuy
{
public function code()
{
return 'coding...';
}
public function test()
{
return 'testing in localhost...';
}
}
class QAEngineer implements SoftwareGuy
{
public function code()
{
// Ignore - I’m not that guy
}
public function test()
{
return 'testing in test server';
}
}
ISP - REFACTORED(1)
interface Coder
{
public function code();
}
interface Tester
{
public function test();
}
class ProjectManagement
{
public function implement(Coder $member)
{
$member->code();
}
public function test(Tester $member)
{
$member->test();
}
}
class Programmer implements Coder, Tester
{
public function code()
{
return 'coding...';
}
public function test()
{
return 'testing in localhost...';
}
}
class QAEngineer implements Tester
{
public function test()
{
return 'testing in test server';
}
}
DEPENDENCY INVERSION PRINCIPLE
Entities must depend
on abstractions not on
concretions
The high level module must not depend
on the low level module, but they should
depend on abstractions
DIP - VIOLATED (1)
class PasswordReminder {
private $dbConnection;
public function __construct(MySQLConnection $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
DIP - VIOLATED (1)
class PasswordReminder {
private $dbConnection;
public function __construct(MySQLConnection $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
➤ MySQLConnection is the low level module
➤ PasswordReminder is high level module
➤ PasswordReminder class is being forced to depend on
the MySQLConnection class
DIP - REFACTORED
interface DBConnectionInterface {
public function connect();
}
class MySQLConnection implements DBConnectionInterface {
public function connect()
{
return "Database connection”;
}
}
class PasswordReminder {
private $dbConnection;
public function __construct(DBConnectionInterface $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
SYMPTOMS OF VIOLATION
➤ A lot of "if" statements to handle different situations in object
➤ A lot of code that doesn't actually do anything, just to
satisfy interface design
➤ You keep opening the same class to change the code
➤ Writing code in classes that don't really have anything to do with
that class. (e,g, DB Query in Email class)
➤ Putting classes of different types of job in same Namespace
QUESTIONS?
Revisiting  SOLID Principles

Revisiting SOLID Principles

  • 1.
    REVISITING SOLID PRINCIPLE at“Eskimi Tech Adda”
  • 2.
    WHO AM I? AnisUddin Ahmad Head of Software Development Devnet Limited.
  • 3.
  • 4.
    LET’S START WITHSOME PREREQUISITES What is the difference between Abstract Class and Interface
  • 5.
    CLASSES - BUILDINGBLOCK OF OOP ➤ Defines and classifies Object types
  • 6.
    CLASSES - BUILDINGBLOCK OF OOP ➤ Produce milk ➤ Have hair* ➤ Give live birth ➤ Two legs ➤ Have feather ➤ Lay eggs walk() giveBirth() fly() layEggs() ➤ Have Backbone ➤ Ability of reproduction
  • 7.
    INTERFACE - DEFINECONTRACTS ➤ Defines a set of abilities ➤ Declares the operations that can be invoked on an object
  • 8.
    SOLID - BASICPRINCIPLES OF OOD
  • 9.
    SINGLE RESPONSIBILITY PRINCIPLE Aclass should have one, and only one, reason to change Responsibility == Reason to Change — Uncle Bob
  • 10.
    SRP EXAMPLE -VIOLATED (1) class SalesReport { private string $title; private array $conditions; private array $connectionParams; public function __construct(string $title, array $conditions, array $connectionParams) { $this->title = $title; $this->conditions = $conditions; $this->connectionParams = $connectionParams; } public function getData() { return [ 'title' => $this->getTitle(), 'generatedOn' => new DateTime(), 'date' => $this->loadReportData($this->conditions, $this->connectionParams), ]; } // Cont…
  • 11.
    SRP EXAMPLE -VIOLATED (2) public function getData() { return [ 'title' => $this->getTitle(), 'generatedOn' => new DateTime(), 'date' => $this->loadReportData($this->conditions, $this->connectionParams), ]; } public function getJsonData() { return json_encode($this->getData()); } private function loadReportData(array $conditions, array $connectionParams) : array { // Create PDO Connection // Query for Report Data based on conditions // return Array result } // Getter/Setter and other functions }
  • 12.
    SRP EXAMPLE -REFACTORED (1) // ———— Data Accessor ————— class DataRepository { public function __construct(array $connectionParams) { /** **/} public function findByID(int $id) { /** **/} public function findByCondition(array $conditions): array { /** **/} private function connectDataSource() { /** **/} } class SalesDataRepository extends DataRepository {/** **/}
  • 13.
    SRP EXAMPLE -REFACTORED (2) // ———— Data Accessor ————— class DataRepository { public function __construct(array $connectionParams) { /** **/} public function findByID(int $id) { /** **/} public function findByCondition(array $conditions): array { /** **/} private function connectDataSource() { /** **/} } class SalesDataRepository extends DataRepository {/** **/} // ———— Content Formatting ————— interface Exportable { public function getData(); } class JSONReportExporter { public function export(Exportable $report) { return json_encode($report->getData()); } }
  • 14.
    SRP EXAMPLE -REFACTORED (3) class SalesReport implements Exportable { private string $title; private array $conditions; private DataRepository $repository; public function __construct(string $title, array $conditions, DataRepository $repository) { $this->title = $title; $this->conditions = $conditions; $this->repository = $repository; } public function getData() { return [ 'title' => $this->getTitle(), 'generatedOn' => new DateTime(), 'data' => $this->repository->findByCondition($this->conditions), ]; } // Removed getJsonData() and loadReportData() // Other relevant methods }
  • 15.
    SRP EXAMPLE -REFACTORED (4) $dataSource = new SalesDataRepository($connectionParams); $conditions = [ 'from' => $startDate, 'to' => $endDate, ]; $salesReport = new SalesReport("Summary of January 2020", $conditions, $dataRepository); $formatter = new JSONReportExporter(); $formatter->export($salesReport); ➤ Cleaner Code ➤ Easier to understand ➤ Easier to change code ➤ Code becomes reusable ➤ Easier to write test codes
  • 16.
    SRP EXAMPLE -REFACTORED (4) $dataSource = new SalesDataRepository($connectionParams); $conditions = [ 'from' => $startDate, 'to' => $endDate, ]; $salesReport = new SalesReport("Summary of January 2020", $conditions, $dataRepository); $formatter = new JSONReportExporter(); $formatter->export($salesReport); ➤ Cleaner Code ➤ Easier to understand ➤ Easier to change code ➤ Code becomes reusable ➤ Easier to write test codes ➤ New features are easy to implement class JSONReportExporter { public function export(Exportable $report) { return json_encode($report->getData()); } } class CSVReportExporter {} { /** collapsed **/} class XMLReportExporter {} { /** collapsed **/} class PDFReportExporter {} { /** collapsed **/}
  • 17.
    OPEN CLOSED PRINCIPLE Aclass should be open for extension, but closed for modification You should be able to extend a classes behaviour, without modifying it.
  • 18.
    OCP EXAMPLE -VIOLATED (1) class Rectangle { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } } class Square { private $length; public function __construct($length) { $this->length = $length; } }
  • 19.
    OCP EXAMPLE -VIOLATED (1) class AreaCalculator { protected $shapes; public function __construct($shapes = array()) { $this->shapes = $shapes; } public function sum() { foreach($this->shapes as $shape) { if($shape instanceof Square) { $area[] = pow($shape->length, 2); } else if($shape instanceof Rectangle) { $area[] = $shape->width * $shape->height; } } return array_sum($area); } }
  • 20.
    OCP EXAMPLE -VIOLATED (1) class AreaCalculator { protected $shapes; public function __construct($shapes = array()) { $this->shapes = $shapes; } public function sum() { foreach($this->shapes as $shape) { if($shape instanceof Square) { $area[] = pow($shape->length, 2); } else if($shape instanceof Rectangle) { $area[] = $shape->width * $shape->height; } } return array_sum($area); } } $shapes = array( new Square(5), new Square(2), new Rectangle(6, 5) ); $calc = new AreaCalculator($shapes); echo $calc->sum();
  • 21.
    OCP EXAMPLE -REFACTORED (1) interface Shape { public function area(); } class AreaCalculator { protected $shapes; public function __construct($shapes = array()) { $this->shapes = $shapes; } public function sum() { foreach($this->shapes as $shape) { $area[] = $shape->area(); } return array_sum($area); } }
  • 22.
    OCP EXAMPLE -REFACTORED (1) class Rectangle implements Shape { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function area() { return $this->width * $this->height; } } class Square implements Shape { private $length; public function __construct($length) { $this->length = $length; } public function area() { return pow($this->length, 2); } }
  • 23.
    class Rectangle implementsShape { private $width; private $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function area() { return $this->width * $this->height; } } class Square implements Shape { private $length; public function __construct($length) { $this->length = $length; } public function area() { return pow($this->length, 2); } } OCP EXAMPLE - REFACTORED (1) class Circle implements Shape { private $radius; public function __construct($radius) { $this->radius = $radius; } public function area() { return pi() * pow($this->radius, 2); } }
  • 24.
    OCP EXAMPLE -REFACTORED (1) interface Shape { public function area(); } class AreaCalculator { protected $shapes; public function __construct($shapes = array()) { $this->shapes = $shapes; } public function sum() { foreach($this->shapes as $shape) { $area[] = $shape->area(); } return array_sum($area); } } $shapes = array( new Square(5), new Square(2), new Rectangle(6, 5), new Circle(7), ); $calc = new AreaCalculator($shapes); echo $calc->sum();
  • 25.
    LISKOV SUBSTITUTION PRINCIPLE Letq(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
  • 26.
    LISKOV SUBSTITUTION PRINCIPLE Letq(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 Objects MUST be replaceable by instances of their subtypes without altering the correct functioning of our system.
  • 27.
    LSP - VIOLATED(1) abstract class LogWriter { abstract function write($message); } class FileWriter extends LogWriter { function write($message){ /** Write to file **/} } class EmailWriter extends LogWriter { function write($message) { $this->sendEmail($message, $this->getReceivers()); } private function sendEmail($content, array $to) {/** **/} } class SlackWriter extends LogWriter { /** **/} class DBWriter extends LogWriter { /** **/}
  • 28.
    LSP - VIOLATED(2) class Logger { private $writers; public function addWriter(LogWriter $writer) { $this->writers[] = $writer; } public function log($message) { foreach ($this->writers as $logWriter) { $logWriter->write($message); } } } $logger = new Logger(); $logger->addWriter(new FileWriter()); $logger->addWriter(new DBWriter()); $logger->addWriter(new EmailWriter()); $logger->log("We are at Eskimi Tech Adda!");
  • 29.
    LSP - VIOLATED(3) class UserRegistration extends EmailWriter { function sendConfirmationEmail($message) { $mailBody = $this->loadTemplate('confirmation.tmpl', $this->getUser()); $this->write($mailBody); } // Overwriting to work with registering user instead of logging address function write($message) { $this->sendEmail($message, $this->getReceivers()); $this->sendEmail($message, $this->getUser()->getEmail()); } }
  • 30.
    LSP - VIOLATED(4) $logger = new Logger(); $logger->addWriter(new FileWriter()); $logger->addWriter(new DBWriter()); $logger->addWriter(new UserRegistration()); $logger->log("We are at Eskimi Tech Adda!"); class UserRegistration extends EmailWriter { function sendConfirmationEmail($message) { $mailBody = $this->loadTemplate('confirmation.tmpl', $this->getUser()); $this->write($mailBody); } // Overwriting to work with registering user instead of logging address function write($message) { $this->sendEmail($message, $this->getReceivers()); $this->sendEmail($message, $this->getUser()->getEmail()); } }
  • 31.
    LSP - VIOLATED(5) $logger = new Logger(); $logger->addWriter(new FileWriter()); $logger->addWriter(new DBWriter()); >> $logger->addWriter(new EmailWriter()); // <— Unexpected behaviour $logger->log("We are at Eskimi Tech Adda!"); class UserRegistration extends EmailWriter { function sendConfirmationEmail($message) { $mailBody = $this->loadTemplate('confirmation.tmpl', $this->getUser()); $this->write($mailBody); } // Overwriting to work with registering user instead of logging address function write($message) { $this->sendEmail($message, $this->getReceivers()); $this->sendEmail($message, $this->getUser()->getEmail()); } }
  • 32.
    INTERFACE SEGREGATION PRINCIPLE Aclient 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
  • 33.
    INTERFACE SEGREGATION PRINCIPLE Aclient 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 Develop small, specific interfaces instead of big, general-purpose interfaces
  • 34.
    ISP - VIOLATED(1) interface SoftwareGuy { public function code(); public function test(); } class ProjectManagement { public function implement(SoftwareGuy $member) { $member->code(); } public function test(SoftwareGuy $member) { $member->test(); } }
  • 35.
    ISP - VIOLATED(2) interface SoftwareGuy { public function code(); public function test(); } class ProjectManagement { public function implement(SoftwareGuy $member) { $member->code(); } public function test(SoftwareGuy $member) { $member->test(); } } class Programmer implements SoftwareGuy { public function code() { return 'coding...'; } public function test() { return 'testing in localhost...'; } } class QAEngineer implements SoftwareGuy { public function code() { // Ignore - I’m not that guy } public function test() { return 'testing in test server'; } }
  • 36.
    ISP - REFACTORED(1) interfaceCoder { public function code(); } interface Tester { public function test(); } class ProjectManagement { public function implement(Coder $member) { $member->code(); } public function test(Tester $member) { $member->test(); } } class Programmer implements Coder, Tester { public function code() { return 'coding...'; } public function test() { return 'testing in localhost...'; } } class QAEngineer implements Tester { public function test() { return 'testing in test server'; } }
  • 37.
    DEPENDENCY INVERSION PRINCIPLE Entitiesmust depend on abstractions not on concretions The high level module must not depend on the low level module, but they should depend on abstractions
  • 38.
    DIP - VIOLATED(1) class PasswordReminder { private $dbConnection; public function __construct(MySQLConnection $dbConnection) { $this->dbConnection = $dbConnection; } }
  • 39.
    DIP - VIOLATED(1) class PasswordReminder { private $dbConnection; public function __construct(MySQLConnection $dbConnection) { $this->dbConnection = $dbConnection; } } ➤ MySQLConnection is the low level module ➤ PasswordReminder is high level module ➤ PasswordReminder class is being forced to depend on the MySQLConnection class
  • 40.
    DIP - REFACTORED interfaceDBConnectionInterface { public function connect(); } class MySQLConnection implements DBConnectionInterface { public function connect() { return "Database connection”; } } class PasswordReminder { private $dbConnection; public function __construct(DBConnectionInterface $dbConnection) { $this->dbConnection = $dbConnection; } }
  • 41.
    SYMPTOMS OF VIOLATION ➤A lot of "if" statements to handle different situations in object ➤ A lot of code that doesn't actually do anything, just to satisfy interface design ➤ You keep opening the same class to change the code ➤ Writing code in classes that don't really have anything to do with that class. (e,g, DB Query in Email class) ➤ Putting classes of different types of job in same Namespace
  • 42.