solid principles
DESCRIPTION
Early in year 2000 Robert C. Martin, aka "Uncle Bob", introduced his thoughts in an article about some of five basic principles on object oriented programming to the open world. What he probably did not expect was, that this 5 principles became the standard for object oriented programming. This Talk is about those principles: Single responsibility, Open/close, Liskov substitution, Interface segregation, and Dependency inversion, or shorter S.O.L.I.D. principles. It will give you an introduction about these principles, their meaning, and where they should be recognized and applied. Examples from my daily work will show you the practical aspects of those principles.TRANSCRIPT
Me, myself & I
PHP since 2001
Testing and Quality coach @ Liip Inc.
Opensource addict
PHP manual translations
FluentDOM
phpDox
News from Uncle Bob
Essential principles for software development & object oriented design (OOD).
Robert C. Martin summarized those principles, but did not invent them.
Which are these principles?
Single responsibility principle
Open/Close principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle
Geolocation Tracker
„As a hiker I want to track where I walked and how much I climbed.“
„As a developer I want to be able to store the geo information on different devices.“
„As a developer I want to store the geo information in a unified format.“
Single responsibilityprinciple (SRP)
„A class should have one, and only one, reason to change.“
How to doit wrong
<?phpnamespace lapis\Tracker;use lapis\Tracker\Structs;
class Geolocation extends Tracker{ public function trackPosition(Position $position) { list($langitude, $longitude, $altitude) = $this->extractCoordinatesFromPosition($position); $altitude = $this->convertFeetToMeter($altitude); $this->persistPosition($langitude, $longitude, $altitude, new DateTime()); }
public function persistPosition( $langitude, $longitude, $altitude, \DateTime $time) { try{ $conn = $this->getDatabaseConnection('write'); $conn->execute($this->generateQuery($langitude, $longitude, $altitude, $time)); } catch (Exception $e) { $this->logError($e->getMessage()); } } /** […] */}
How to doit right
<?php
namespace lapis\Tracker;use lapis\Tracker\Structs\Position;
class Geolocation extends Tracker{ public function __construct(Parser $parser, PersistenceManager $pm) { /** […] */ }
public function trackPosition(Position $position, \DateTime $time = null) { try { $coordinates = $this->parser->parsePosition($position); $this->pm->persistPosition($coordinates, $time); } catch (PersistanceManagerException $e) { throw new TrackerException( 'Unable to persist your position.', TrackerException::PersistError, $e ); } }}
Single responsibilityprinciple
Simple to understand, very hard to get right.
Separation of responsibilities / concerns
One responsibility per class
Liskov substitutionprinciple (LSP)
„Derived classes must be substitutable for their base
classes.“
Where dowe start
<?phpnamespace lapis\Converter;
class Distance { const FACTOR = 0.3048; // 1 foot in meters
public function feetToMeters($distance) { $this->verifyDistance($distance); return $distance * self::FACTOR; }
public function metersToFeet($distance) { $this->verifyDistance($distance); return $distance / self::FACTOR; }
protected function verifyDistance($distance) { if ($distance < 0) { throw new \OutOfRangeException( 'Distance may not be lower than zero.', DistanceException::OutOfRange ); } }}
How to doit wrong
<?php
namespace lapis\Converter\Distance;use lapis\Converter;
class NegativeDistance extends Distance{ protected function verifyDistance($distance) { return TRUE; }}
How to doit right
<?phpnamespace lapis\Converter\Distance;use lapis\Converter;
class MaxDistance extends Distance { public function feetToMeters($distance) { $distance = parent::feetToMeters($distance); $this->verifyDistance($distance, 15000); return $distance; }
protected function verifyDistance($distance, $max = 0) { if ($distance < 0) { $message = 'Distance may not be lower than the zero.'; } if ($max > 0 && $distance >= $max) { $message = 'Distance may not be greater than the maximum of ' . $max . '.'; } If (isset($message) { throw new \OutOfRangeException($message, DistanceException::OutOfRange); } }}
Liskov substitutionprinciple
Design by contract
User must not distinguish between super- & subclasses
Derived class must be more strict on output, but may handle the input less strict.
Increases maintainability, robustness & resusability
Dependency inversionprinciple (DIP)
„Depend on abstractions, not on concretions.“
How to doit wrong
<?phpnamespace lapis\Tracker;use lapis\Tracker\Structs\Position;
class PersistenceManager { public function __construct(Tracker $tracker) { /** […] */ }
public function trackPosition(Position $position) { try { $this->tracker->trackPosition($position); $this->log('Position stored successfully'); } catch (TrackerException $e) { $this->log($e->getMessage()); } }
protected function log($message) { $fh = fopen ('log.txt' , 'a'); fwrite($fh, $message); fclose($fh); } /** […] */}
How to doit right
<?php
namespace lapis\Tracker;use lapis\Logger, lapis\Tracker\Services;
class PersistenceManager{ public function __construct(PersistService $ps, Logger $logger) { /** […] */ }
public function persistPosition($coordinates, \DateTime $time = null) { try { $this->ps->setCoordinates($coordinates); $this->ps->setTimeStamp($time); $this->ps->persist(); $this->logger->log('Position stored successfully');
} catch (PersistServiceException $e) { $this->logger->exception($e); } }}
Dependency inversionprinciple
Fundamental principle for OOD
Encapsulate low level modules in abstractions
Depend on abstractions / interfaces rather than implementations
Interface segregationprinciple (ISP)
„Make fine grained interfaces that are client specific.“
Where dowe start
<?php
namespace lapis\Tracker;use lapis\Logger, lapis\Tracker\Services;
class PersistenceManager{ public function __construct(PersistService $ps, Logger $logger) { /** […] */ }
public function persistPosition($coordinates, \DateTime $time = null) { try { $this->ps->setCoordinates($coordinates); $this->ps->setTimeStamp($time); $this->ps->persist(); $this->logger->log('Position stored successfully');
} catch (PersistServiceException $e) { $this->logger->exception($e); } }}
How to doit right
<?php
namespace lapis\Tracker;use lapis\Logger, lapis\Tracker\Services;
class PersistenceManager implements PersistenceManagerPosition{ public function __construct(PersistService $ps, Logger $logger) { /** […] */ }
public function persistPosition($coordinates, \DateTime $time = null) { /** […] */ }
public function persistHeight($height, \DateTime $time = null) { /** […] */ }
public function persistLocation($height, \DateTime $time = null) { /** […] */ }}
interface PersistenceManagerPosition{ public function persistPosition($coordinates, \DateTime $time = null);}
<?php
namespace lapis\Tracker;use lapis\Tracker\Structs\Position;
class Geolocation extends Tracker{ public function __construct(Parser $parser, PersistenceManagerPosition $pm) { /** […] */ }
public function trackPosition(Position $position, \DateTime $time = null) { try{ $coordinates = $this->parser->parsePosition($position); $this->pm->persistPosition($coordinates, $time); } catch (PersistanceManagerException $e) { throw new TrackerException( 'Unable to persist your position.', TrackerException::PersistError, $e ); } }}
Interface segregationprinciple
Avoid „fat“ interfaces, stick to what's really needed
Ask yourself „What do I want to archieve?“
Be strict!
Open/Close principle (OCP)
„You should be able to extend a classes behavior, without modifying
it.“
Open/Close principle (OCP)
„It is the heard of object oriented design“
Combines the other 4 principles
Where we started
Where we got
Questions@lapistano
PHP5.3 Powerworkshop
New features of PHP5.3
Best Pratices using OOP
PHPUnit
PHPDocumentor
License
This set of slides and the source code included in the download package is licensed under the
Creative Commons Attribution-Noncommercial-Share Alike 2.0 Generic License
http://creativecommons.org/licenses/by-nc-sa/2.0/deed.en