S.O.L.I.D
                        Principles



Bastian Feder                        OSIDays 2011, Bangalore
bastian.feder@liip.ch                    21st November 2011
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 responsibility
 principle (SRP)



„A class should have one, and only
     one, reason to change.“
How to do
it wrong
<?php
namespace lapisTracker;
use lapisTrackerStructs;

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 do
it right
<?php

namespace lapisTracker;
use lapisTrackerStructsPosition;

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 responsibility
 principle
 Simple to understand, very hard to get right.
 Separation of responsibilities / concerns
 One responsibility per class
Liskov substitution
  principle (LSP)



   „Derived classes must be
  substitutable for their base
           classes.“
Where do
we start
<?php
namespace lapisConverter;

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 do
it wrong
<?php

namespace lapisConverterDistance;
use lapisConverter;

class NegativeDistance extends Distance
{
   protected function verifyDistance($distance)
   {
     return TRUE;
   }
}
How to do
it right
<?php
namespace lapisConverterDistance;
use lapisConverter;

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 substitution
  principle
 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 inversion
 principle (DIP)



 „Depend on abstractions, not on
         concretions.“
How to do
it wrong
<?php
namespace lapisTracker;
use lapisTrackerStructsPosition;

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 do
it right
<?php

namespace lapisTracker;
use lapisLogger, lapisTrackerServices;

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 inversion
 principle
 Fundamental principle for OOD
 Encapsulate low level modules in abstractions
 Depend on abstractions / interfaces rather than
 implementations
Interface segregation
  principle (ISP)



„Make fine grained interfaces that
      are client specific.“
Where do
we start
<?php

namespace lapisTracker;
use lapisLogger, lapisTrackerServices;

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 do
it right
<?php

namespace lapisTracker;
use lapisLogger, lapisTrackerServices;

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 lapisTracker;
use lapisTrackerStructsPosition;

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 segregation
  principle
 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

bastian.feder@liip.ch
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

Solid principles

  • 1.
    S.O.L.I.D Principles Bastian Feder OSIDays 2011, Bangalore bastian.feder@liip.ch 21st November 2011
  • 2.
    Me, myself &I PHP since 2001 Testing and Quality coach @ Liip Inc. Opensource addict PHP manual translations FluentDOM phpDox
  • 3.
    News from UncleBob Essential principles for software development & object oriented design (OOD). Robert C. Martin summarized those principles, but did not invent them.
  • 4.
    Which are theseprinciples? Single responsibility principle Open/Close principle Liskov substitution principle Interface segregation principle Dependency inversion principle
  • 5.
    Geolocation Tracker „As ahiker 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.“
  • 6.
    Single responsibility principle(SRP) „A class should have one, and only one, reason to change.“
  • 7.
  • 8.
    <?php namespace lapisTracker; use lapisTrackerStructs; classGeolocation 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()); } } /** […] */ }
  • 9.
  • 10.
    <?php namespace lapisTracker; use lapisTrackerStructsPosition; classGeolocation 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 ); } } }
  • 11.
    Single responsibility principle Simple to understand, very hard to get right. Separation of responsibilities / concerns One responsibility per class
  • 12.
    Liskov substitution principle (LSP) „Derived classes must be substitutable for their base classes.“
  • 13.
  • 14.
    <?php namespace lapisConverter; 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 ); } } }
  • 15.
  • 16.
    <?php namespace lapisConverterDistance; use lapisConverter; classNegativeDistance extends Distance { protected function verifyDistance($distance) { return TRUE; } }
  • 17.
  • 18.
    <?php namespace lapisConverterDistance; use lapisConverter; classMaxDistance 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); } } }
  • 19.
    Liskov substitution principle 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
  • 20.
    Dependency inversion principle(DIP) „Depend on abstractions, not on concretions.“
  • 21.
  • 22.
    <?php namespace lapisTracker; use lapisTrackerStructsPosition; classPersistenceManager { 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); } /** […] */ }
  • 23.
  • 24.
    <?php namespace lapisTracker; use lapisLogger,lapisTrackerServices; 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); } } }
  • 25.
    Dependency inversion principle Fundamental principle for OOD Encapsulate low level modules in abstractions Depend on abstractions / interfaces rather than implementations
  • 26.
    Interface segregation principle (ISP) „Make fine grained interfaces that are client specific.“
  • 27.
  • 28.
    <?php namespace lapisTracker; use lapisLogger,lapisTrackerServices; 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); } } }
  • 29.
  • 30.
    <?php namespace lapisTracker; use lapisLogger,lapisTrackerServices; 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); }
  • 31.
    <?php namespace lapisTracker; use lapisTrackerStructsPosition; classGeolocation 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 ); } } }
  • 32.
    Interface segregation principle Avoid „fat“ interfaces, stick to what's really needed Ask yourself „What do I want to archieve?“ Be strict!
  • 33.
    Open/Close principle (OCP) „You should be able to extend a classes behavior, without modifying it.“
  • 34.
    Open/Close principle (OCP) „It is the heard of object oriented design“ Combines the other 4 principles
  • 35.
  • 36.
  • 37.
  • 38.
    PHP5.3 Powerworkshop New features of PHP5.3 Best Pratices using OOP PHPUnit PHPDocumentor
  • 39.
    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