Download - Solid principles
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