SlideShare a Scribd company logo
SOLID PRINCIPLES
LUCIANO QUEIROZ
SOLID PRINCIPLES
DECISIONS
SOLID PRINCIPLES
SOLID
PRINCIPLES
Robert Martin aka Uncle Bob
CHANGES
SOLID PRINCIPLES
SOLID PRINCIPLES
SINGLE RESPONSIBILITY PRINCIPLE
“A CLASS SHOULD HAVE ONE, 

AND ONLY ONE, REASON TO
CHANGE.”
SOLID PRINCIPLES
<?php
class ConfirmationMailMailer
{
private $templating;
private $mailer;
public function __construct(
ITemplatingEngine $templating,
IMailer $mailer
) {
$this->templating = $templating;
$this->mailer = $mailer;
}
public function sendTo(User $user)
{
$message = $this->createMessageFor($user);
$this->sendMessage($message);
}
private function createMessageFor(User $user)
{
$subject = 'Confirm your email address';
$body = $this->templating
->render(
'confirmationMail.html.tpl', [
'confirmationCode' => $user->getConfirmationCode()
]
);
$message = new Message($subject, $body);
$message->setTo($user->getEmailAdress());
return $message;
}
private function sendMessage(IMessage $message)
{
$this->mailer->send($message);
}
SOLID PRINCIPLES
INITIAL SITUATION
ConfirmationMailMailer.php
IMailer.php
usesuses
ITemplatingEngine.php
SOLID PRINCIPLES
RESPONSIBILITIES
▸ Create Confirmation Message
▸ Send Confirmation Message to user
SINGLE RESPONSIBILITY PRINCIPLE
“RESPONSIBILITIES ARE
REASONS TO CHANGE”
SOLID PRINCIPLES
SOLID PRINCIPLES
SINGLE RESPONSIBILITY PRINCIPLE VIOLATIONS
▸ Many instance variables
▸ Many public methods
▸ Each method use variables from other instance
▸ Specific tasks are done by private methods
COLLABORATOR
CLASSES
<?php
class ConfirmationMailFactory
{
private $templating;
public function __construct(ITemplatingEngine $templating)
{
$this->templating = $templating;
}
public function createMessageFor(User $user)
{
$subject = 'Confirm your email address';
$body = $this->templating
->render(
'confirmationMail.html.tpl', [
'confirmationCode' => $user->getConfirmationCode()
]
);
$message = new Message($subject, $body);
$message->setTo($user->getEmailAdress());
return $message;
}
}
<?php
class ConfirmationMailMailer
{
private $confirmationMailFactory;
private $mailer;
public function __construct(
ConfirmationMailFactory $confirmationMailFactory,
IMailer $mailer
) {
$this->confirmationMailFactory = $confirmationMailFactory;
$this->mailer = $mailer;
}
public function sendTo(User $user)
{
$message = $this->confirmationMailFactory->createMessageFor($user);
$this->sendMessage($message);
}
private function sendMessage(IMessage $message)
{
$this->mailer->send($message);
}
}
SOLID PRINCIPLES
REFACTORED
ConfirmationMailMailer.php IMailer.phpuses
ConfirmationMailFactory.php
uses
ITemplatingEngine.phpuses
SOLID PRINCIPLES
SINGLE RESPONSIBILITY PRINCIPLE ADVANTAGES
▸ Smaller classes
▸ Classes easier to understand
▸ Classes easier to test
▸ Classes simpler to maintain
OPEN/CLOSED PRINCIPLE
“YOU SHOULD BE ABLE TO
EXTEND A CLASS`S BEHAVIOR,
WITHOUT MODIFYING IT.”
SOLID PRINCIPLES
<?php
class GenericEncoder
{
public function encodeToFormat($data, $format)
{
if ($format === 'json') {
$encoder = new JsonEncoder();
} elseif ($format === 'xml') {
$encoder = new XmlEncoder();
} else {
throw new InvalidArgumentException('Unknown format');
}
$data = $this->prepareData($data, $format);
return $encoder->encode($data);
}
private function prepareData($data, $format)
{
switch ($format) {
case 'json':
$data = $this->forceArrayKeys($data);
$data = $this->fixKeys($data);
break;
case 'xml':
$data = $this->fixAttributes($data);
break;
default:
throw new InvalidArgumentException(
'Format not supported'
);
}
return $data;
}
}
SOLID PRINCIPLES
INITIAL SITUATION
GenericEncoder.php
XmlEncoder.php
uses
uses
JsonEncoder.php
<?php
class GenericEncoder
{
public function encodeToFormat($data, $format)
{
if (…) {
…
} elseif (…) {
…
} elseif ($format === 'yaml') {
$encoder = new YamlEncoder();
} else {
throw new InvalidArgumentException('Unknown format');
}
}
…
}
SOLID PRINCIPLES
NEW REQUIREMENT SITUATION
GenericEncoder.php
XmlEncoder.php
uses
uses
JsonEncoder.php
YamlEncoder.php
uses
OPEN/CLOSED PRINCIPLE
“A UNIT OF CODE CAN BE
CONSIDERED ‘OPEN’ FOR EXTENSION
WHEN ITS BEHAVIOR CAN BE EASILY
CHANGED WITHOUT MODIFYING IT”
SOLID PRINCIPLES
SOLID PRINCIPLES
OPEN/CLOSED PRINCIPLE VIOLATIONS
▸ Conditions to determine a strategy
▸ Conditions using the same variables or constants are
recurring inside the class or related classes
▸ Contains hard-coded references to other classes or class
names
▸ Objects are being created using the new operator
ABSTRACT
FACTORY
<?php
interface EncoderInterface
{
public function encode($data);
}
class JsonEncoder implements EncoderInterface
{
public function encode($data)
{
// TODO: Implement encode() method.
}
}
class XmlEncoder implements EncoderInterface
{
public function encode($data)
{
// TODO: Implement encode() method.
}
}
class YamlEncoder implements EncoderInterface
{
public function encode($data)
{
// TODO: Implement encode() method.
}
}
SOLID PRINCIPLES
INTRODUCING ENCODER INTERFACE
GenericEncoder.php
XmlEncoder.php
uses
JsonEncoder.phpYamlEncoder.php
implements
EncoderInterface.php
implements
implements
<?php
class EncoderFactory
{
public function createForFormat($format)
{
if ($format === 'json') {
return new JsonEncoder();
} elseif ($format === 'xml') {
return new XmlEncoder();
} elseif ($format === 'yaml') {
return new YamlEncoder();
}
throw new InvalidArgumentException('Unknown format');
}
}
<?php
class GenericEncoder
{
private $encoderFactory;
public function __construct(EncoderFactory $encoderFactory)
{
$this->encoderFactory = $encoderFactory;
}
public function encodeToFormat($data, $format)
{
$encoder = $this->encoderFactory->createForFormat($format);
$data = $this->prepareData($data, $format);
return $encoder->encode($data);
}
}
ABSTRACT FACTORY
OPEN FOR EXTENSION
SOLID PRINCIPLES
<?php
interface EncoderFactoryInterface
{
/**
* Create an encoder for the given format
*
* @param string $format
* @return EncoderInterface
*/
public function createForFormat($format)
}
class EncoderFactory implements EncoderFactoryInterface
{
...
}
class GenericEncoder
{
public function __construct(
EncoderFactoryInterface $encoderFactory
) {
...
}
...
}
INTRODUCING DEPENDENCY INVERSION PRINCIPLE
SOLID PRINCIPLES
GenericEncoder.php
XmlEncoder.php
uses
JsonEncoder.phpYamlEncoder.php
implements
EncoderInterface.php
implements
implements
EncoderFactoryInterface.php
uses
creates
<?php
class EncoderFactory implements EncoderFactoryInterface
{
private $factories = [];
/**
* Register a callable that returns an instance of
* EncoderInterface for the given format.
*
* @param string $format
* @param callable $factory
*/
public function addEncoderFactory($format, callable $factory)
{
$this->factories[$format] = $factory;
}
public function createForFormat($format)
{
$factory = $this->factories[$format];
$encoder = $factory;
return $encoder;
}
}
<?php
$encoderFactory = new EncoderFactory();
$encoderFactory->addEncoderFactory(
'xml',
function () {
return new XmlEncoder();
}
);
$encoderFactory->addEncoderFactory(
'json',
function () {
return new JsonEncoder();
}
);
$genericEncoder = new GenericEncoder($encoderFactory);
$data = ...
$jsonEncodedData = $genericEncoder->encode($data, 'json');
<?php
class GenericEncoder
{
private function prepareData($data, $format)
{
switch ($format) {
case 'json':
$data = $this->forceArray($data);
$data = $this->fixKeys($data);
break;
case 'xml':
$data = $this->fixAttributes($data);
break
default:
# code...
break;
}
return $data;
}
}
PORLYMORPHISM
<?php
class GenericEncoder
{
private $encoderFactory;
public function __construct(
EncoderFactoryInterface $encoderFactory
) {
$this->encoderFactory;
}
public function encodeToFormat($data, $format)
{
$encoder = $this->encoderFactory->createForFormat($format);
$data = $encoder->prepareData($data);
return $encoder->encode($data);
}
}
<?php
class JsonEncoder implements EncoderInterface
{
public function encode($data)
{
...
}
public function prepareData($data)
{
$data = $this->forceArray($data);
$data = $this->fixKeys($data);
return $data;
}
}
<?php
class GenericEncoder
{
private $encoderFactory;
public function __construct(
EncoderFactoryInterface $encoderFactory
) {
$this->encoderFactory;
}
public function encodeToFormat($data, $format)
{
$encoder = $this->encoderFactory->createForFormat($format);
$data = $encoder->prepareData($data);
return $encoder->encode($data);
}
}
<?php
class JsonEncoder implements EncoderInterface
{
public function encode($data)
{
$data = $this->prepareData($data);
return json_encode($data);
}
private function prepareData($data)
{
$data = $this->forceArray($data);
$data = $this->fixKeys($data);
return $data;
}
}
<?php
interface EncoderInterface
{
public function encode($data);
}
<?php
class GenericEncoder
{
public function encodeToFormat($data, $format)
{
if ($format === 'json') {
$encoder = new JsonEncoder();
} elseif ($format === 'xml') {
$encoder = new XmlEncoder();
} else {
throw new InvalidArgumentException('Unknown format');
}
$data = $this->prepareData($data, $format);
return $encoder->encode($data);
}
private function prepareData($data, $format)
{
switch ($format) {
case 'json':
$data = $this->forceArrayKeys($data);
$data = $this->fixKeys($data);
break;
case 'xml':
$data = $this->fixAttributes($data);
break;
default:
throw new InvalidArgumentException(
'Format not supported'
);
}
return $data;
}
}
<?php
class GenericEncoder
{
private $encoderFactory;
public function __construct(
EncoderFactoryInterface $encoderFactory
) {
$this->encoderFactory;
}
public function encodeToFormat($data, $format)
{
$encoder = $this->encoderFactory->createForFormat($format);
return $encoder->encode($data);
}
}
SOLID PRINCIPLES
LISKOV SUBSTITUTION PRINCIPLE
“DERIVED CLASSES MUST
BE SUBSTITUTABLE FOR
THEIR BASE CLASSES”
SOLID PRINCIPLES
SOLID PRINCIPLES
WE NEED TO UNDERSTAND
▸ Derived classes
▸ Being substitutable
DERIVED
CLASSES
<?php
/**
* A concrete class, all methods are implemented, but can be
* overridden by derived classes
*/
class ConcreteClass
{
public function implementedMethod()
{
}
}
/**
* An abstract class, some methods need to be implemented by
* by derived classes
*/
abstract class AbstractClass
{
abstract public function abstractMethod();
public function implementedMethod()
{
}
}
/**
* An interface, all methods need to be implemented by derived
* classes
*/
interface AnInterface
{
public function abstractMethod();
}
BEING
SUBSTITUTABLE
VIOLATIONS
LISKOV SUBSTITUTION PRINCIPLE
“A DERIVED CLASS DOES NOT
HAVE AN IMPLEMENTATION
FOR ALL METHODS”
SOLID PRINCIPLES
<?php
interface FileInterface
{
public function rename($name);
public function changeOwner($user, $group);
}
<?php
class DropboxFile implements FileInterface
{
public function rename($name)
{
...
}
public function changeOwner($user, $group)
{
throw new BadMethodCallException(
"Not implemented for Dropbox files"
);
}
}
<?php
class DropboxFile implements FileInterface
{
public function rename($name)
{
...
}
public function changeOwner($user, $group)
{
throw new BadMethodCallException(
"Not implemented for Dropbox files"
);
}
}
//ask the user to check if $file is an instance of DropboxFile
if (!($file instanceof DropboxFile)) {
$file->changeOwner(...);
}
<?php
class DropboxFile implements FileInterface
{
...
public function changeOwner($user, $group)
{
//let's do nothing :)
}
}
SOLID PRINCIPLES
INTERFACE SEGREGATION PRINCIPLE
“MAKE FINE-GRAINED
INTERFACES THAT ARE
CLIENT SPECIFIC”
SOLID PRINCIPLES
<?php
interface FileInterface
{
public function rename($name);
}
interface FileWithOwnerInterface extends FileInterface
{
public function changeOwner($user, $group);
}
<?php
class DropboxFile implements FileInterface
{
public function rename($name)
{
...
}
}
class LocalFile implements FileWithOwnerInterface
{
public function rename($name)
{
...
}
public function changeOwner($user, $group)
{
...
}
}
SOLID PRINCIPLES
NEW HIERARCHY OF FILE CLASSES
DropboxFile.php
extends
LocalFile.php
implements
FileInterface.php
implements
FileWithOwnerInterface.php
LISKOV SUBSTITUTION PRINCIPLE
“DIFFERENT SUBSTITUTES
RETURN THINGS OF
DIFFERENT TYPES”
SOLID PRINCIPLES
SOLID PRINCIPLES
<?php
interface RouterInterface
{
public function getRoutes() : RouterCollection;
}
class SimpleRouter implements RouterInterface
{
public function getRoutes()
{
$routes = [];
//add Route objects to $routes
$routes[] = ...;
return $routes
}
}
class AdvancedRouter implements RouterInterface
{
public function getRoutes()
{
$routeCollection = new RouteCollection();
...
return $routeCollection;
}
}
<?php
interface RouterInterface
{
public function getRoutes() : RouterCollection;
}
class SimpleRouter implements RouterInterface
{
public function getRoutes() : RouteCollection
{
$routes = [];
//add Route objects to $routes2
$routes[] = ...;
return $routes
}
}
class AdvancedRouter implements RouterInterface
{
public function getRoutes() : RouteCollection
{
$routeCollection = new RouteCollection();
...
return $routeCollection;
}
}
LISKOV SUBSTITUTION PRINCIPLE
“A DERIVED CLASS IS LESS
PERMISSIVE WITH REGARD
TO METHOD ARGUMENTS”
SOLID PRINCIPLES
<?php
interface MassMailerInterface
{
public function sendMail(
TransportInterface $transport,
MessageInterface $message,
RecipientsInterface $recipients
);
}
<?php
class SmtpMassMailer implements MassMailerInterface
{
public function sendMail(
TransportInterface $transport,
MessageInterface $message,
RecipientsInterface $recipients
) {
if (!($transport instanceof SmtpTransport)) {
throw new InvalidArgumentException(
'SmtpMassMailer only works with SMTP'
);
}
}
}
LISKOV SUBSTITUTION PRINCIPLE
“NOT EVERY KIND OF MAIL
TRANSPORT IS SUITABLE
FOR MASS MAILING”
SOLID PRINCIPLES
SOLID PRINCIPLES
NEW CLASS DIAGRAM
SmtpTransport.php
TransportInterface.php
extends
TransportWithMassMailSupportInterface.php
implements
<?php
interface MassMailerInterface
{
public function sendMail(
TransportWithMassMailSupportInterface $transport,
MessageInterface $message,
RecipientsInterface $recipients
);
}
<?php
class SmtpMassMailer implements MassMailerInterface
{
public function sendMail(
TransportWithMassMailSupportInterface $transport,
MessageInterface $message,
RecipientsInterface $recipients
) {
…
}
}
SOLID PRINCIPLES
A GOOD SUBSTITUTE
▸ Provides an implementation for all the methods of the
base class.
▸ Returns the type of things the base class prescribes.
▸ Doesn’t put extra constraints on arguments for methods.
INTERFACE SEGREGATION PRINCIPLE
“MAKE FINE-GRAINED
INTERFACES THAT ARE
CLIENT SPECIFIC”
SOLID PRINCIPLES
DEPENDENCY INVERSION PRINCIPLE
“DEPEND ON ABSTRACTIONS,
NOT CONCRETIONS”
SOLID PRINCIPLES
SOLID PRINCIPLES
FIZZ BUZZ ASSIGNMENT
▸ Generate a list of integers, from 1 to n.
▸ Numbers that are divisible by 3 should be replace with
“Fizz”
▸ Numbers that are divisible by 5 should be replaced with
“Buzz”
▸ Numbers that are both divisible by 3 and by 5 should be
replaced with “FizzBuzz”
<?php
class FizzBuzz
{
public static function generateList($limit)
{
$list = [];
for ($number = 1; $number <= $limit; $number++) {
$list[] = self::generateElement($number);
}
return $list;
}
private static function generateElement($number)
{
if ($number % 3 === 0 && $number % 5 === 0) {
return 'FizzBuzz';
}
if ($number % 3 === 0) {
return 'Fizz';
}
if ($number % 5 === 0) {
return 'Buzz';
}
return $number;
}
}
SOLID PRINCIPLES
THEN…
▸ It should be possible to add another rule, without
modifying the FizzBuzz class.
MAKING FIZZBUZZ
OPEN FOR EXTENSION
<?php
class FizzBuzz
{
public static function generateList($limit)
{
...
}
private static function generateElement($number)
{
$fizzBuzzRule = new FizzBuzzRule();
if ($fizzBuzzRule->matches($number)) {
return $fizzBuzzRule->getReplacement();
}
$fizzRule = new FizzRule();
if ($fizzRule->matches($number)) {
return $fizzRule->getReplacement();
}
$buzzRule = new BuzzRule();
if ($buzzRule->matches($number)) {
return $buzzRule->getReplacement();
}
return $number;
}
}
<?php
interface RuleInterface
{
public function matches($number);
public function getReplacement();
}
<?php
class FizzBuzz
{
private $rules = [];
public function addRule(RuleInterface $rule) {
$this->rules[] = $rule;
}
public static function generateList($limit)
{
...
}
private static function generateElement($number)
{
foreach ($this->rules as $rule) {
if ($rule->matches($number)) {
return $rule->getReplacement();
}
}
return $number;
}
}
<?php
class FizzBuzzRule implements RuleInterface
{
...
}
class FizzRule implements RuleInterface
{
...
}
class BuzzRule implements RuleInterface
{
...
}
<?php
$fizzBuzz = new FizzBuzz();
$fizzBuzz->addRule(new FizzBuzzRule());
$fizzBuzz->addRule(new FizzRule());
$fizzBuzz->addRule(new BuzzRule());
...
$list = $fizzBuzz->generateList(100);
SOLID PRINCIPLES
HAVING CONCRETE DEPENDENCIES
FizzBuzz.php
FizzRule.php
BuzzRule.php
FizzBuzzRule.php
SOLID PRINCIPLES
HAVING ABSTRACT DEPENDENCIES
FizzBuzz.php RuleInterface.php
BuzzRule.phpFizzBuzzRule.php FizzRule.php
DEPENDENCY INVERSION PRINCIPLE VIOLATION
“MIXING DIFFERENT
LEVELS OF ABSTRACTION”
SOLID PRINCIPLES
<?php
use DoctrineDBALConnection;
class Authentication
{
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function checkCredentials($username, $password)
{
$user = $this->connection->fetchAssoc(
'SELECT * FROM users WHERE username = ?',
[$username]
);
if ($user === null) {
throw new InvalidCredentialsException(
"User not found."
);
}
//validate password
...
}
}
SOLID PRINCIPLES
AUTHENTICATION CLASS DEPENDS ON CONNECTION
Authentication.php Connection.php
<?php
class Authentication
{
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function checkCredentials($username, $password)
{
$user = $this->userRepository->ofUsername($username);
if ($user === null) {
throw new InvalidCredentialsException(
"User not found."
);
}
//validate password
...
}
}
<?php
class UserRepository
{
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function ofUsername($username)
{
return $this->connection->fetchAssoc(
'SELECT * FROM users WHERE username = ?',
[$username]
);
}
}
SOLID PRINCIPLES
AUTHENTICATION CLASS DEPENDS ON USER REPOSITORY
Authentication.php UserRepository.php Connection.php
<?php
interface UserRepositoryInterface
{
public function ofUsername($username);
}
<?php
class DoctrineDbalUserRepository implements UserRepositoryInterface
{
...
}
class TextFileUserRepository implements UserRepositoryInterface
{
...
}
class InMemoryUserRepository implements UserRepositoryInterface
{
...
}
class MongoDbUserRepository implements UserRepositoryInterface
{
...
}
<?php
class Authentication
{
private $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
}
SOLID PRINCIPLES
AUTHENTICATION CLASS DEPENDS ON USER REPOSITORY INTERFACE
Authentication.php UserRepositoryInterface.php
TextFileUserRepository.php DoctrineDbalUserRepository.php Connection.php

More Related Content

PDF
7 rules of simple and maintainable code
PPT
Advanced Javascript
PDF
Solid Principles
PDF
SOLID Design Principles applied in Java
PPT
SOLID Design Principles
PPT
JavaScript Tutorial
PDF
Kiss PageObjects [01-2017]
PDF
Vue.js for beginners
7 rules of simple and maintainable code
Advanced Javascript
Solid Principles
SOLID Design Principles applied in Java
SOLID Design Principles
JavaScript Tutorial
Kiss PageObjects [01-2017]
Vue.js for beginners

What's hot (20)

PDF
Writing clean code
PPTX
Form Handling using PHP
PPTX
Solid design principles
PPTX
Clean Code
PPTX
The SOLID Principles Illustrated by Design Patterns
KEY
Solid principles
PPTX
Clean Code II - Dependency Injection
PPTX
Solid principles
PDF
Introduction to SOLID Principles
PPTX
Reactjs
PDF
Intro to vue.js
PPTX
Clean Code: Chapter 3 Function
PPTX
Design principles - SOLID
PDF
Introduction To Angular's reactive forms
KEY
Object Calisthenics Applied to PHP
PPTX
Virtual function in C++ Pure Virtual Function
PDF
Vue, vue router, vuex
PDF
JUnit & Mockito, first steps
PPTX
Solid principles
PDF
Clean code
Writing clean code
Form Handling using PHP
Solid design principles
Clean Code
The SOLID Principles Illustrated by Design Patterns
Solid principles
Clean Code II - Dependency Injection
Solid principles
Introduction to SOLID Principles
Reactjs
Intro to vue.js
Clean Code: Chapter 3 Function
Design principles - SOLID
Introduction To Angular's reactive forms
Object Calisthenics Applied to PHP
Virtual function in C++ Pure Virtual Function
Vue, vue router, vuex
JUnit & Mockito, first steps
Solid principles
Clean code
Ad

Viewers also liked (20)

PPTX
Refactoring Applications using SOLID Principles
PPT
Solid principles
PPT
SOLID principles
PDF
NoSQL and CouchDB
PPTX
Relax, it's spa time
PDF
SOLID design principles in Ruby
ODP
Geecon09: SOLID Design Principles
PPTX
SOLID principles
PPTX
Do we need SOLID principles during software development?
PDF
SOLID Design principles
PPTX
Ada Lovelace - Candidata ao Oscar das Mulheres de TI!
PDF
Refactoring to SOLID Code
PPTX
Mulheres na TI - Rails Girls SP 2015
PPT
Mulheres na TI: o que mudou?
PDF
A Checklist for Design Reviews
PDF
Applying Design Principles in Practice
PPTX
SOLID Principles part 1
PPTX
SOLID Principles part 2
PPTX
S.O.L.I.D. Principles for Software Architects
PDF
Object Oriented Design Principles
Refactoring Applications using SOLID Principles
Solid principles
SOLID principles
NoSQL and CouchDB
Relax, it's spa time
SOLID design principles in Ruby
Geecon09: SOLID Design Principles
SOLID principles
Do we need SOLID principles during software development?
SOLID Design principles
Ada Lovelace - Candidata ao Oscar das Mulheres de TI!
Refactoring to SOLID Code
Mulheres na TI - Rails Girls SP 2015
Mulheres na TI: o que mudou?
A Checklist for Design Reviews
Applying Design Principles in Practice
SOLID Principles part 1
SOLID Principles part 2
S.O.L.I.D. Principles for Software Architects
Object Oriented Design Principles
Ad

Similar to SOLID PRINCIPLES (20)

PDF
Writing SOLID code (in practice)
PDF
Be pragmatic, be SOLID
PDF
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
PPTX
PDF
Demystifying Object-Oriented Programming #phpbnl18
PDF
Demystifying Object-Oriented Programming - PHP[tek] 2017
PDF
Some OOP paradigms & SOLID
PDF
Demystifying Object-Oriented Programming - Lone Star PHP
PDF
Nikita Popov "What’s new in PHP 8.0?"
PDF
What's new in PHP 8.0?
PDF
Workshop: Refactoring Legacy PHP: The Complete Guide
PDF
OOP in PHP
PDF
Living With Legacy Code
PDF
Demystifying Object-Oriented Programming - ZendCon 2016
PDF
Don't Be STUPID, Grasp SOLID - DrupalCon Prague
PDF
DocBlox: your source matters @ #pfc11
PDF
Dependency Injection for PHP
PDF
Developing SOLID Code
PDF
How I started to love design patterns
PDF
Demystifying Object-Oriented Programming - PHP UK Conference 2017
Writing SOLID code (in practice)
Be pragmatic, be SOLID
4Developers 2015: Be pragmatic, be SOLID - Krzysztof Menżyk
Demystifying Object-Oriented Programming #phpbnl18
Demystifying Object-Oriented Programming - PHP[tek] 2017
Some OOP paradigms & SOLID
Demystifying Object-Oriented Programming - Lone Star PHP
Nikita Popov "What’s new in PHP 8.0?"
What's new in PHP 8.0?
Workshop: Refactoring Legacy PHP: The Complete Guide
OOP in PHP
Living With Legacy Code
Demystifying Object-Oriented Programming - ZendCon 2016
Don't Be STUPID, Grasp SOLID - DrupalCon Prague
DocBlox: your source matters @ #pfc11
Dependency Injection for PHP
Developing SOLID Code
How I started to love design patterns
Demystifying Object-Oriented Programming - PHP UK Conference 2017

Recently uploaded (20)

PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
medical staffing services at VALiNTRY
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PPTX
CHAPTER 2 - PM Management and IT Context
PPTX
Operating system designcfffgfgggggggvggggggggg
PPTX
history of c programming in notes for students .pptx
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPTX
Odoo POS Development Services by CandidRoot Solutions
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PPTX
L1 - Introduction to python Backend.pptx
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
System and Network Administration Chapter 2
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
medical staffing services at VALiNTRY
Navsoft: AI-Powered Business Solutions & Custom Software Development
CHAPTER 2 - PM Management and IT Context
Operating system designcfffgfgggggggvggggggggg
history of c programming in notes for students .pptx
How Creative Agencies Leverage Project Management Software.pdf
Odoo POS Development Services by CandidRoot Solutions
ManageIQ - Sprint 268 Review - Slide Deck
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Odoo Companies in India – Driving Business Transformation.pdf
2025 Textile ERP Trends: SAP, Odoo & Oracle
L1 - Introduction to python Backend.pptx
VVF-Customer-Presentation2025-Ver1.9.pptx
PTS Company Brochure 2025 (1).pdf.......
Wondershare Filmora 15 Crack With Activation Key [2025
System and Network Administration Chapter 2
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus

SOLID PRINCIPLES

  • 9. SINGLE RESPONSIBILITY PRINCIPLE “A CLASS SHOULD HAVE ONE, 
 AND ONLY ONE, REASON TO CHANGE.” SOLID PRINCIPLES
  • 10. <?php class ConfirmationMailMailer { private $templating; private $mailer; public function __construct( ITemplatingEngine $templating, IMailer $mailer ) { $this->templating = $templating; $this->mailer = $mailer; } public function sendTo(User $user) { $message = $this->createMessageFor($user); $this->sendMessage($message); } private function createMessageFor(User $user) { $subject = 'Confirm your email address'; $body = $this->templating ->render( 'confirmationMail.html.tpl', [ 'confirmationCode' => $user->getConfirmationCode() ] ); $message = new Message($subject, $body); $message->setTo($user->getEmailAdress()); return $message; } private function sendMessage(IMessage $message) { $this->mailer->send($message); }
  • 12. SOLID PRINCIPLES RESPONSIBILITIES ▸ Create Confirmation Message ▸ Send Confirmation Message to user
  • 13. SINGLE RESPONSIBILITY PRINCIPLE “RESPONSIBILITIES ARE REASONS TO CHANGE” SOLID PRINCIPLES
  • 14. SOLID PRINCIPLES SINGLE RESPONSIBILITY PRINCIPLE VIOLATIONS ▸ Many instance variables ▸ Many public methods ▸ Each method use variables from other instance ▸ Specific tasks are done by private methods
  • 16. <?php class ConfirmationMailFactory { private $templating; public function __construct(ITemplatingEngine $templating) { $this->templating = $templating; } public function createMessageFor(User $user) { $subject = 'Confirm your email address'; $body = $this->templating ->render( 'confirmationMail.html.tpl', [ 'confirmationCode' => $user->getConfirmationCode() ] ); $message = new Message($subject, $body); $message->setTo($user->getEmailAdress()); return $message; } }
  • 17. <?php class ConfirmationMailMailer { private $confirmationMailFactory; private $mailer; public function __construct( ConfirmationMailFactory $confirmationMailFactory, IMailer $mailer ) { $this->confirmationMailFactory = $confirmationMailFactory; $this->mailer = $mailer; } public function sendTo(User $user) { $message = $this->confirmationMailFactory->createMessageFor($user); $this->sendMessage($message); } private function sendMessage(IMessage $message) { $this->mailer->send($message); } }
  • 19. SOLID PRINCIPLES SINGLE RESPONSIBILITY PRINCIPLE ADVANTAGES ▸ Smaller classes ▸ Classes easier to understand ▸ Classes easier to test ▸ Classes simpler to maintain
  • 20. OPEN/CLOSED PRINCIPLE “YOU SHOULD BE ABLE TO EXTEND A CLASS`S BEHAVIOR, WITHOUT MODIFYING IT.” SOLID PRINCIPLES
  • 21. <?php class GenericEncoder { public function encodeToFormat($data, $format) { if ($format === 'json') { $encoder = new JsonEncoder(); } elseif ($format === 'xml') { $encoder = new XmlEncoder(); } else { throw new InvalidArgumentException('Unknown format'); } $data = $this->prepareData($data, $format); return $encoder->encode($data); } private function prepareData($data, $format) { switch ($format) { case 'json': $data = $this->forceArrayKeys($data); $data = $this->fixKeys($data); break; case 'xml': $data = $this->fixAttributes($data); break; default: throw new InvalidArgumentException( 'Format not supported' ); } return $data; } }
  • 23. <?php class GenericEncoder { public function encodeToFormat($data, $format) { if (…) { … } elseif (…) { … } elseif ($format === 'yaml') { $encoder = new YamlEncoder(); } else { throw new InvalidArgumentException('Unknown format'); } } … }
  • 24. SOLID PRINCIPLES NEW REQUIREMENT SITUATION GenericEncoder.php XmlEncoder.php uses uses JsonEncoder.php YamlEncoder.php uses
  • 25. OPEN/CLOSED PRINCIPLE “A UNIT OF CODE CAN BE CONSIDERED ‘OPEN’ FOR EXTENSION WHEN ITS BEHAVIOR CAN BE EASILY CHANGED WITHOUT MODIFYING IT” SOLID PRINCIPLES
  • 26. SOLID PRINCIPLES OPEN/CLOSED PRINCIPLE VIOLATIONS ▸ Conditions to determine a strategy ▸ Conditions using the same variables or constants are recurring inside the class or related classes ▸ Contains hard-coded references to other classes or class names ▸ Objects are being created using the new operator
  • 28. <?php interface EncoderInterface { public function encode($data); } class JsonEncoder implements EncoderInterface { public function encode($data) { // TODO: Implement encode() method. } } class XmlEncoder implements EncoderInterface { public function encode($data) { // TODO: Implement encode() method. } } class YamlEncoder implements EncoderInterface { public function encode($data) { // TODO: Implement encode() method. } }
  • 29. SOLID PRINCIPLES INTRODUCING ENCODER INTERFACE GenericEncoder.php XmlEncoder.php uses JsonEncoder.phpYamlEncoder.php implements EncoderInterface.php implements implements
  • 30. <?php class EncoderFactory { public function createForFormat($format) { if ($format === 'json') { return new JsonEncoder(); } elseif ($format === 'xml') { return new XmlEncoder(); } elseif ($format === 'yaml') { return new YamlEncoder(); } throw new InvalidArgumentException('Unknown format'); } }
  • 31. <?php class GenericEncoder { private $encoderFactory; public function __construct(EncoderFactory $encoderFactory) { $this->encoderFactory = $encoderFactory; } public function encodeToFormat($data, $format) { $encoder = $this->encoderFactory->createForFormat($format); $data = $this->prepareData($data, $format); return $encoder->encode($data); } }
  • 34. <?php interface EncoderFactoryInterface { /** * Create an encoder for the given format * * @param string $format * @return EncoderInterface */ public function createForFormat($format) } class EncoderFactory implements EncoderFactoryInterface { ... } class GenericEncoder { public function __construct( EncoderFactoryInterface $encoderFactory ) { ... } ... }
  • 35. INTRODUCING DEPENDENCY INVERSION PRINCIPLE SOLID PRINCIPLES GenericEncoder.php XmlEncoder.php uses JsonEncoder.phpYamlEncoder.php implements EncoderInterface.php implements implements EncoderFactoryInterface.php uses creates
  • 36. <?php class EncoderFactory implements EncoderFactoryInterface { private $factories = []; /** * Register a callable that returns an instance of * EncoderInterface for the given format. * * @param string $format * @param callable $factory */ public function addEncoderFactory($format, callable $factory) { $this->factories[$format] = $factory; } public function createForFormat($format) { $factory = $this->factories[$format]; $encoder = $factory; return $encoder; } }
  • 37. <?php $encoderFactory = new EncoderFactory(); $encoderFactory->addEncoderFactory( 'xml', function () { return new XmlEncoder(); } ); $encoderFactory->addEncoderFactory( 'json', function () { return new JsonEncoder(); } ); $genericEncoder = new GenericEncoder($encoderFactory); $data = ... $jsonEncodedData = $genericEncoder->encode($data, 'json');
  • 38. <?php class GenericEncoder { private function prepareData($data, $format) { switch ($format) { case 'json': $data = $this->forceArray($data); $data = $this->fixKeys($data); break; case 'xml': $data = $this->fixAttributes($data); break default: # code... break; } return $data; } }
  • 40. <?php class GenericEncoder { private $encoderFactory; public function __construct( EncoderFactoryInterface $encoderFactory ) { $this->encoderFactory; } public function encodeToFormat($data, $format) { $encoder = $this->encoderFactory->createForFormat($format); $data = $encoder->prepareData($data); return $encoder->encode($data); } }
  • 41. <?php class JsonEncoder implements EncoderInterface { public function encode($data) { ... } public function prepareData($data) { $data = $this->forceArray($data); $data = $this->fixKeys($data); return $data; } }
  • 42. <?php class GenericEncoder { private $encoderFactory; public function __construct( EncoderFactoryInterface $encoderFactory ) { $this->encoderFactory; } public function encodeToFormat($data, $format) { $encoder = $this->encoderFactory->createForFormat($format); $data = $encoder->prepareData($data); return $encoder->encode($data); } }
  • 43. <?php class JsonEncoder implements EncoderInterface { public function encode($data) { $data = $this->prepareData($data); return json_encode($data); } private function prepareData($data) { $data = $this->forceArray($data); $data = $this->fixKeys($data); return $data; } }
  • 45. <?php class GenericEncoder { public function encodeToFormat($data, $format) { if ($format === 'json') { $encoder = new JsonEncoder(); } elseif ($format === 'xml') { $encoder = new XmlEncoder(); } else { throw new InvalidArgumentException('Unknown format'); } $data = $this->prepareData($data, $format); return $encoder->encode($data); } private function prepareData($data, $format) { switch ($format) { case 'json': $data = $this->forceArrayKeys($data); $data = $this->fixKeys($data); break; case 'xml': $data = $this->fixAttributes($data); break; default: throw new InvalidArgumentException( 'Format not supported' ); } return $data; } }
  • 46. <?php class GenericEncoder { private $encoderFactory; public function __construct( EncoderFactoryInterface $encoderFactory ) { $this->encoderFactory; } public function encodeToFormat($data, $format) { $encoder = $this->encoderFactory->createForFormat($format); return $encoder->encode($data); } }
  • 48. LISKOV SUBSTITUTION PRINCIPLE “DERIVED CLASSES MUST BE SUBSTITUTABLE FOR THEIR BASE CLASSES” SOLID PRINCIPLES
  • 49. SOLID PRINCIPLES WE NEED TO UNDERSTAND ▸ Derived classes ▸ Being substitutable
  • 51. <?php /** * A concrete class, all methods are implemented, but can be * overridden by derived classes */ class ConcreteClass { public function implementedMethod() { } } /** * An abstract class, some methods need to be implemented by * by derived classes */ abstract class AbstractClass { abstract public function abstractMethod(); public function implementedMethod() { } } /** * An interface, all methods need to be implemented by derived * classes */ interface AnInterface { public function abstractMethod(); }
  • 54. LISKOV SUBSTITUTION PRINCIPLE “A DERIVED CLASS DOES NOT HAVE AN IMPLEMENTATION FOR ALL METHODS” SOLID PRINCIPLES
  • 55. <?php interface FileInterface { public function rename($name); public function changeOwner($user, $group); }
  • 56. <?php class DropboxFile implements FileInterface { public function rename($name) { ... } public function changeOwner($user, $group) { throw new BadMethodCallException( "Not implemented for Dropbox files" ); } }
  • 57. <?php class DropboxFile implements FileInterface { public function rename($name) { ... } public function changeOwner($user, $group) { throw new BadMethodCallException( "Not implemented for Dropbox files" ); } } //ask the user to check if $file is an instance of DropboxFile if (!($file instanceof DropboxFile)) { $file->changeOwner(...); }
  • 58. <?php class DropboxFile implements FileInterface { ... public function changeOwner($user, $group) { //let's do nothing :) } }
  • 60. INTERFACE SEGREGATION PRINCIPLE “MAKE FINE-GRAINED INTERFACES THAT ARE CLIENT SPECIFIC” SOLID PRINCIPLES
  • 61. <?php interface FileInterface { public function rename($name); } interface FileWithOwnerInterface extends FileInterface { public function changeOwner($user, $group); }
  • 62. <?php class DropboxFile implements FileInterface { public function rename($name) { ... } } class LocalFile implements FileWithOwnerInterface { public function rename($name) { ... } public function changeOwner($user, $group) { ... } }
  • 63. SOLID PRINCIPLES NEW HIERARCHY OF FILE CLASSES DropboxFile.php extends LocalFile.php implements FileInterface.php implements FileWithOwnerInterface.php
  • 64. LISKOV SUBSTITUTION PRINCIPLE “DIFFERENT SUBSTITUTES RETURN THINGS OF DIFFERENT TYPES” SOLID PRINCIPLES
  • 66. <?php interface RouterInterface { public function getRoutes() : RouterCollection; } class SimpleRouter implements RouterInterface { public function getRoutes() { $routes = []; //add Route objects to $routes $routes[] = ...; return $routes } } class AdvancedRouter implements RouterInterface { public function getRoutes() { $routeCollection = new RouteCollection(); ... return $routeCollection; } }
  • 67. <?php interface RouterInterface { public function getRoutes() : RouterCollection; } class SimpleRouter implements RouterInterface { public function getRoutes() : RouteCollection { $routes = []; //add Route objects to $routes2 $routes[] = ...; return $routes } } class AdvancedRouter implements RouterInterface { public function getRoutes() : RouteCollection { $routeCollection = new RouteCollection(); ... return $routeCollection; } }
  • 68. LISKOV SUBSTITUTION PRINCIPLE “A DERIVED CLASS IS LESS PERMISSIVE WITH REGARD TO METHOD ARGUMENTS” SOLID PRINCIPLES
  • 69. <?php interface MassMailerInterface { public function sendMail( TransportInterface $transport, MessageInterface $message, RecipientsInterface $recipients ); }
  • 70. <?php class SmtpMassMailer implements MassMailerInterface { public function sendMail( TransportInterface $transport, MessageInterface $message, RecipientsInterface $recipients ) { if (!($transport instanceof SmtpTransport)) { throw new InvalidArgumentException( 'SmtpMassMailer only works with SMTP' ); } } }
  • 71. LISKOV SUBSTITUTION PRINCIPLE “NOT EVERY KIND OF MAIL TRANSPORT IS SUITABLE FOR MASS MAILING” SOLID PRINCIPLES
  • 72. SOLID PRINCIPLES NEW CLASS DIAGRAM SmtpTransport.php TransportInterface.php extends TransportWithMassMailSupportInterface.php implements
  • 73. <?php interface MassMailerInterface { public function sendMail( TransportWithMassMailSupportInterface $transport, MessageInterface $message, RecipientsInterface $recipients ); }
  • 74. <?php class SmtpMassMailer implements MassMailerInterface { public function sendMail( TransportWithMassMailSupportInterface $transport, MessageInterface $message, RecipientsInterface $recipients ) { … } }
  • 75. SOLID PRINCIPLES A GOOD SUBSTITUTE ▸ Provides an implementation for all the methods of the base class. ▸ Returns the type of things the base class prescribes. ▸ Doesn’t put extra constraints on arguments for methods.
  • 76. INTERFACE SEGREGATION PRINCIPLE “MAKE FINE-GRAINED INTERFACES THAT ARE CLIENT SPECIFIC” SOLID PRINCIPLES
  • 77. DEPENDENCY INVERSION PRINCIPLE “DEPEND ON ABSTRACTIONS, NOT CONCRETIONS” SOLID PRINCIPLES
  • 78. SOLID PRINCIPLES FIZZ BUZZ ASSIGNMENT ▸ Generate a list of integers, from 1 to n. ▸ Numbers that are divisible by 3 should be replace with “Fizz” ▸ Numbers that are divisible by 5 should be replaced with “Buzz” ▸ Numbers that are both divisible by 3 and by 5 should be replaced with “FizzBuzz”
  • 79. <?php class FizzBuzz { public static function generateList($limit) { $list = []; for ($number = 1; $number <= $limit; $number++) { $list[] = self::generateElement($number); } return $list; } private static function generateElement($number) { if ($number % 3 === 0 && $number % 5 === 0) { return 'FizzBuzz'; } if ($number % 3 === 0) { return 'Fizz'; } if ($number % 5 === 0) { return 'Buzz'; } return $number; } }
  • 80. SOLID PRINCIPLES THEN… ▸ It should be possible to add another rule, without modifying the FizzBuzz class.
  • 82. <?php class FizzBuzz { public static function generateList($limit) { ... } private static function generateElement($number) { $fizzBuzzRule = new FizzBuzzRule(); if ($fizzBuzzRule->matches($number)) { return $fizzBuzzRule->getReplacement(); } $fizzRule = new FizzRule(); if ($fizzRule->matches($number)) { return $fizzRule->getReplacement(); } $buzzRule = new BuzzRule(); if ($buzzRule->matches($number)) { return $buzzRule->getReplacement(); } return $number; } }
  • 83. <?php interface RuleInterface { public function matches($number); public function getReplacement(); }
  • 84. <?php class FizzBuzz { private $rules = []; public function addRule(RuleInterface $rule) { $this->rules[] = $rule; } public static function generateList($limit) { ... } private static function generateElement($number) { foreach ($this->rules as $rule) { if ($rule->matches($number)) { return $rule->getReplacement(); } } return $number; } }
  • 85. <?php class FizzBuzzRule implements RuleInterface { ... } class FizzRule implements RuleInterface { ... } class BuzzRule implements RuleInterface { ... }
  • 86. <?php $fizzBuzz = new FizzBuzz(); $fizzBuzz->addRule(new FizzBuzzRule()); $fizzBuzz->addRule(new FizzRule()); $fizzBuzz->addRule(new BuzzRule()); ... $list = $fizzBuzz->generateList(100);
  • 87. SOLID PRINCIPLES HAVING CONCRETE DEPENDENCIES FizzBuzz.php FizzRule.php BuzzRule.php FizzBuzzRule.php
  • 88. SOLID PRINCIPLES HAVING ABSTRACT DEPENDENCIES FizzBuzz.php RuleInterface.php BuzzRule.phpFizzBuzzRule.php FizzRule.php
  • 89. DEPENDENCY INVERSION PRINCIPLE VIOLATION “MIXING DIFFERENT LEVELS OF ABSTRACTION” SOLID PRINCIPLES
  • 90. <?php use DoctrineDBALConnection; class Authentication { private $connection; public function __construct(Connection $connection) { $this->connection = $connection; } public function checkCredentials($username, $password) { $user = $this->connection->fetchAssoc( 'SELECT * FROM users WHERE username = ?', [$username] ); if ($user === null) { throw new InvalidCredentialsException( "User not found." ); } //validate password ... } }
  • 91. SOLID PRINCIPLES AUTHENTICATION CLASS DEPENDS ON CONNECTION Authentication.php Connection.php
  • 92. <?php class Authentication { private $userRepository; public function __construct(UserRepository $userRepository) { $this->userRepository = $userRepository; } public function checkCredentials($username, $password) { $user = $this->userRepository->ofUsername($username); if ($user === null) { throw new InvalidCredentialsException( "User not found." ); } //validate password ... } }
  • 93. <?php class UserRepository { private $connection; public function __construct(Connection $connection) { $this->connection = $connection; } public function ofUsername($username) { return $this->connection->fetchAssoc( 'SELECT * FROM users WHERE username = ?', [$username] ); } }
  • 94. SOLID PRINCIPLES AUTHENTICATION CLASS DEPENDS ON USER REPOSITORY Authentication.php UserRepository.php Connection.php
  • 96. <?php class DoctrineDbalUserRepository implements UserRepositoryInterface { ... } class TextFileUserRepository implements UserRepositoryInterface { ... } class InMemoryUserRepository implements UserRepositoryInterface { ... } class MongoDbUserRepository implements UserRepositoryInterface { ... }
  • 97. <?php class Authentication { private $userRepository; public function __construct(UserRepositoryInterface $userRepository) { $this->userRepository = $userRepository; } }
  • 98. SOLID PRINCIPLES AUTHENTICATION CLASS DEPENDS ON USER REPOSITORY INTERFACE Authentication.php UserRepositoryInterface.php TextFileUserRepository.php DoctrineDbalUserRepository.php Connection.php