Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Design Patterns in PHP (PHPCon Italia 2009)

Design Patterns in PHP (PHPCon Italia 2009)

An introduction to Design Patterns and reusable OOP techniques with PHP. This presentation analyzes the most common design patterns with their respective PHP 5 implementation. Each pattern is discussed and supported by live examples.

@ PHPCon Italia, 2009

Simone Carletti

March 20, 2009
Tweet

More Decks by Simone Carletti

Other Decks in Programming

Transcript

  1. Simone Carletti, Altura Labs
    [email protected]
    Design Patterns in PHP

    View Slide

  2. About me
      Technical Manager in Altura Labs
      The first Zend Framework Italian contributor
      Addicted to Agile Development and Development Best Practices
      Passionate about Object Oriented Programming
      I love pizza
    Simone Carletti, Altura Labs

    View Slide

  3. .. and what about you?
    Simone Carletti, Altura Labs

    View Slide

  4. Simone Carletti, Altura Labs
    What’s wrong with this code?
    Nothing…
    …except all!
    Hard Coded Actions
    Presentation mixed
    with business logic
    Global Variables
    Output mixed with
    elaboration
    Context and responsibility conflicts

    View Slide

  5. Designing object oriented software is hard,
    and designing reusable object-oriented
    software is even harder.
    Design Patters (GoF)
    Simone Carletti, Altura Labs

    View Slide

  6. Design Patterns
    Simone Carletti, Altura Labs

    View Slide

  7. Design Patterns describe simple and
    elegant solutions to specific problems in
    object oriented software design.
    Simone Carletti, Altura Labs

    View Slide

  8. Christopher Alexander
    Simone Carletti, Altura Labs
    Each pattern describes a problem which occurs
    over and over again in our environment, and then
    describes the core of the solution to that problem,
    in such a way that you can use this solution a
    million times over, without ever doing it the same
    way twice.

    View Slide

  9. Christopher Alexander
    Simone Carletti, Altura Labs
    Each pattern is a three-part rule, which
    expresses a relation between a certain
    context, a problem and a solution.

    View Slide

  10. The Gang of Four
    Simone Carletti, Altura Labs

    View Slide

  11. Essential Elements
      Pattern name
      A descriptive name for the pattern, in a word or two
      Problem
      Describes the problem the pattern applies to
      Solution
      Describes the elements and the resources along with their relationship and
    responsibilities for solving the problem.
      This is just an abstract description, the implementation varies according to
    the programming language
      Consequences
      Results and trade-off
    Simone Carletti, Altura Labs

    View Slide

  12. What are Design Patterns
      Solution to common development problems
      Reusable successful design and architectures
      Common language for developers
    Simone Carletti, Altura Labs

    View Slide

  13. What Design Patterns are not
      The panacea for solving all development problems
      Ready to use code scripts
      Cut & Paste solutions
    Simone Carletti, Altura Labs

    View Slide

  14. Why Design Patterns?
      Help you to reuse successful design and architectures
      Help you to choose design alternatives
      Help you to solve problems you haven’t seen before
      Give a common vocabulary
      Let you communicate quickly and unambiguously
      Make a system more reusable
    Simone Carletti, Altura Labs

    View Slide

  15. Design for Changes
    The key to maximizing reuse lies in anticipating
    new requirements and changes to existing
    requirements, and in designing your systems so
    that they can evolve accordingly.
    Simone Carletti, Altura Labs
    Design Patterns (GoF)

    View Slide

  16. Golden Rules
    1.  Program to an interface, not an implementation
    1.  Some languages takes this rule to the next level with Duck Typing
    2.  Favor Object Composition over Inheritance
    1.  Black Box approach
    3.  Delegate
    Simone Carletti, Altura Labs

    View Slide

  17. How to Select a Design Pattern
      Consider how to design patterns solve design problems
      Scan Intent section
      Study how patterns interrelate
      Study patterns of like purpose
      Examine a cause of redesign
      Consider what should be variable in your design
    Simone Carletti, Altura Labs

    View Slide

  18. How to Use a Design Pattern
      Read the pattern once through for an overview
      Go back and study the Structure, Participants and Collaboration
    sections
      Look at the Sample Code section to see a concrete example of the
    pattern in code
      Choose names for pattern participants that are meaningful in the
    application context
      Define the classes
      Define application-specific names for operations in the pattern
      Implement the operations to carry out the responsibilities and
    collaborations in the patterns
    Simone Carletti, Altura Labs

    View Slide

  19. How to NOT Use a Design Pattern
      Don’t try to rewrite your application to fit a Design Pattern, select
    the best Pattern according to your needs
      Don’t use a Design Pattern because it’s cool… use a Pattern
    because you need it
      Don’t copy/paste language-specific Pattern implementations,
    understand how the Pattern works and how you can implement it in
    your application
    Simone Carletti, Altura Labs

    View Slide

  20. A word of warning
    Ideally, when the appropriate problem comes
    along, you should trigger the design pattern and
    your problem is solved.
    Simone Carletti, Altura Labs
    Russ Olsen, Design Patterns in Ruby

    View Slide

  21. patterns : oop = notable products : algebra
    The question isn't whether you'll encounter most patterns
    but whether you'll recognize them when they cross your
    path. Will you know how to cleanly solve the problem
    represented by the pattern, or will you stumble thought
    several code iterations before you find an acceptable
    solutions?
    Simone Carletti, Altura Labs
    Ship it!

    View Slide

  22. Design for Changes
    Do you remember? But we careful…
    Simone Carletti, Altura Labs

    View Slide

  23. Avoid Over-Engineering
    Simone Carletti, Altura Labs

    View Slide

  24. Avoid Under-Engineering
    Simone Carletti, Altura Labs

    View Slide

  25. Don’t fall into the
    “everything is a pattern” trap
    Simone Carletti, Altura Labs

    View Slide

  26. Frameworks vs Design Patterns
      Design patterns are more abstract than frameworks
      Design patterns are smaller architectural elements than
    frameworks
      Design patterns are less specialized than frameworks
      A typical framework contains several design patterns, but the
    reverse is never true
    Simone Carletti, Altura Labs

    View Slide

  27. Helpful Agile methodologies
      Refactoring
      Do you really need this Pattern now?
      You Ain't Gonna Need It
      Refactoring to Pattern
      Unit Test
      Always have a consistent test suite before refactoring your code
      Test-Driven Development
    Simone Carletti, Altura Labs

    View Slide

  28. Design Pattern Classification
    Simone Carletti, Altura Labs

    View Slide

  29. Design Patterns by Purpose
      Creational Patterns
      Concern the process of object creation
      Structural Patterns
      Deal with the composition of object and classes
      Behavioral Patterns
      Characterize the way in which classes or objects interact
      Concurrency Patterns
      Architectural Patterns
      …
    Simone Carletti, Altura Labs

    View Slide

  30. Design Patterns by Scope
      Class Patterns
    Deal with relationships between classes and their subclasses.
    Ex. Factory Method, Adapter, Template method…
      Object Patterns
    Deal with object relationships.
    The most part of patterns are in the object scope.
    Ex. Singleton, Adapter, Decorator, Proxy, Iterator, Observer…
    Simone Carletti, Altura Labs

    View Slide

  31. Design Patterns
      Architectural
      MVC
      Creational
      Singleton
      Factory Method
      Lazy initialization
      Structural
      Adapter
      Proxy
      Behavioral
      Iterator
      Observer
      Strategy
      Template Method
      More
      Registry
      Mock Object
    Simone Carletti, Altura Labs

    View Slide

  32. Q & A
    (part 1/2)
    Simone Carletti, Altura Labs

    View Slide

  33. Design Patterns in Action
    Simone Carletti, Altura Labs

    View Slide

  34. Pattern Template
      Problem & Solution
      Considerations
      In pictures
      Implementation & Examples
      In the Wild
      Using and Abusing
    Simone Carletti, Altura Labs

    View Slide

  35. MVC
    Simone Carletti, Altura Labs

    View Slide

  36. MVC: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You want an efficient design
    solution to split the view from
    the business logic of your
    application
      You want to keep your
    application reusable by
    splitting the design in multiple
    layers
      The MVC pattern decouples
    views and models by
    establishing a subscribe/notify
    protocol between them
      A view reflects the state of the
    model
      This approach enables you to
    attach multiple view to a
    model an provide different
    representations

    View Slide

  37. MVC: in pictures
    Simone Carletti, Altura Labs
    http://betterexplained.com/articles/intermediate-rails-understanding-models-
    views-and-controllers/

    View Slide

  38. MVC: Model, View and Controller
    Simone Carletti, Altura Labs
      The model is the domain-specific representation of data. It usually
    consists in a database or some other storage system.
      The view renders the model in a form suitable according to the
    request. This layer is commonly known with the word template.
      The controller processes the requests and it’s in charge of querying
    the model and returning the right template according to the request.

    View Slide

  39. MVC: decoupling View and Model
    Simone Carletti, Altura Labs
    Articles
    Article::find()
    XML Feed iPhone
    (X)HTML
    $articles
    $articles
    $articles

    View Slide

  40. MVC: in the wild
    Simone Carletti, Altura Labs
      The most part of PHP web frameworks implement the MVC
    pattern, including Symfony, Zend Framework, CakePHP…
      The most part of modern PHP applications inherit some principles
    from MVC pattern.
      Many applications use a template engine
      Many applications separate the model from the view and interact with the
    database via ORM frameworks

    View Slide

  41. MVC: Single Responsibility Principle
    Simone Carletti, Altura Labs
      Every object should have a single responsibility, and that all its
    services should be narrowly aligned with that responsibility.
      The most frequent error using MVC is to execute operations in the
    wrong context

    View Slide

  42. MVC: find the error (1)
    Simone Carletti, Altura Labs
    class BookController
    {
    public function SuggestionsAction()
    {
    $firstChoice = Book::findAll(
    array(
    'conditions' => array('name LIKE ?', '%'.$params['query']),
    'order' => 'weight DESC'
    )
    );
    $secondChoice = Book::findAll(
    array(
    'conditions' => array('name LIKE ?', '%'.$params['query'].'%'),
    'order' => 'weight DESC'
    )
    );
    $this->choices = array_merge($firstChoice, $secondChoice);
    $this->render('suggestions');
    }
    }

    View Slide

  43. MVC: find the error (1/solution)
    Simone Carletti, Altura Labs
    // BookController represents the Book controller
    class BookController
    {
    public function SuggestionsAction()
    {
    $this->choices = Book::findSuggestionsForKeyword($params['query']);
    $this->render('suggestions');
    }
    }
    // Book class represent the Book model
    class Book
    {
    public static function findSuggestionsForKeyword($query) {
    $firstChoice = self::findAll(
    array(
    'conditions' => array('name LIKE ?', '%'.$params['query']),
    'order' => 'weight DESC'
    )
    );
    $secondChoice = self::findAll(
    array(
    'conditions' => array('name LIKE ?', '%'.
    $params['query'].'%'),
    'order' => 'weight DESC'
    )
    );
    return array_merge($firstChoice, $secondChoice);
    }
    }

    View Slide

  44. MVC: find the error (2)
    Simone Carletti, Altura Labs
    Available Tickets
    $tickets = Ticket::findAll(array('conditions' => array('published' => true)));
    $available = array();
    foreach($tickets as $ticket) {
    if ($currentUser->isAdmin() == true || $ticket->isPublic()) {
    $available[] = $ticket;
    }
    }
    ?>


    title == '' ?
    'This ticket has no title' :
    $ticket->title . ' ' . $ticket->price; ?>


    View Slide

  45. MVC: find the error (2/solution)
    Simone Carletti, Altura Labs
    // define formatters in your model
    // only when formatters are not tied to
    // one or more specific views
    class Ticket
    {
    // ...
    public function getFormattedTitle()
    {
    return $ticket->title == '' ?
    'This ticket has no title' :
    $ticket->title .
    ' ' .
    $ticket->price;
    }
    }
    // or delegate the responsibility to the view using
    helpers
    class TicketsHelper
    {
    public static function formatTitle($ticket)
    {
    return $ticket->title == '' ?
    'This ticket has no title' :
    $ticket->title .
    ' ' .
    $ticket->price;
    }
    }
    Available Tickets



    getFormattedTitle(); ?>




    Available Tickets







    // the controller knows about User's ACL
    // and runs the appropriate query through the model
    class TicketsController
    {
    public function AvailableAction()
    {
    if ($currentUser->isAdmin()) {
    $tickets = Ticket::findAll();
    } else {
    $tickets = Ticket::findAllPublic();
    }
    $this->ticket = $tickets;
    }
    }
    Controller
    View
    Model
    Helper

    View Slide

  46. Singleton
    The Highlander
    Simone Carletti, Altura Labs

    View Slide

  47. Singleton: problem & solution
    Problem
      You want a class to have
    exactly one instance
      You want the instance to be
    easily accessible
      You don’t want to use global
    variables
    Solution
      The Singleton pattern ensures
    a class has only one instance,
    and provide a global point to
    access it
    Simone Carletti, Altura Labs

    View Slide

  48. Singleton: considerations
    Simone Carletti, Altura Labs
      Often used for shared objects such as configurations, queues,
    database connections…
      You don’t want the environment to be responsible of class
    instantiation
      You don’t want the environment to be responsible of avoid multiple
    class instances
      The Singleton is often a mixed behavior

    View Slide

  49. Singleton: implementation
    Simone Carletti, Altura Labs
    class Singleton

    {

    // holds the singleton instance

    private static $_instance = null;

    // redefined as private to be called only 

    // from within the class scope

    private function __construct()

    {

    }

    // redefined to deny object clones

    public function __clone()

    {

    throw new Exception('You cannot clone Singleton object');

    }

    public function getInstance()

    {

    if (null === self::$_instance) {

    self::$_instance = new Singleton();

    }

    return self::$_instance;

    }

    }

    View Slide

  50. Singleton: example
    Simone Carletti, Altura Labs
    class Singleton

    {

    // holds the singleton instance

    private static $_instance = null;

    // redefined as private to be called only 

    // from within the class scope

    private function __construct()

    {

    }

    // redefined to deny object clones

    public function __clone()

    {

    throw new Exception('You cannot clone Singleton object');

    }

    public function getInstance()

    {

    if (null === self::$_instance) {

    self::$_instance = new Singleton();

    }

    return self::$_instance;

    }

    }

    $instance = Singleton::getInstance();

    $instance->doSomething();

    Singleton::getInstance()->doSomething();

    Singleton::getInstance()->doSomething()->thenSomethingElse();


    View Slide

  51. Singleton: Class as Singleton
    Simone Carletti, Altura Labs
      You can declare methods as static and use the class as the
    container for the Singleton functionality
      You are sure no one will create additional instances
      You can’t take advantage of other patterns, such as Lazy
    Initialization
      You don’t have control over initialization
      You don’t have access to the instance context
      It’s not thread safe

    View Slide

  52. Singleton: Why not using Global Variables?
    Simone Carletti, Altura Labs
      There’s no way to control the value of a global variable
      Doesn’t prevent someone from creating multiple instances
      Global variables are unpredictable
      Global variables are difficult to debug and test
      Global variables are unsecure
      Global variable makes the code difficult to read

    View Slide

  53. Singleton: in the wild
    Simone Carletti, Altura Labs
      Pake, the Symfony PHP-make library
      Doctrine_Manager, the base component of all Doctrine based
    projects. It opens and keeps track of all database connections
      Many components of the Symfony framework, including
      sfContext
      sfAutoload
      sfCultureInfo
      The configuration class ConfigFile in phpMyAdmin

    View Slide

  54. Singleton: using & abusing
    Simone Carletti, Altura Labs
      Before applying a Singleton ask yourself: do I really need a
    Singleton?
      Look at your code, check the number of a class instances
      Beware to not spread the Singleton knowledge in classes where
    you don’t really need to
      To all intents and purposes, a Singleton instance is a normal class
    instance, you can pass it as a parameter

    View Slide

  55. Factory Method
    Simone Carletti, Altura Labs

    View Slide

  56. Factory Method: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You want to create an
    instance of a class but you
    don’t know in advance the
    object class you need to use
      You want to localize the
    knowledge of which class
    must be instantiated
      The Factory Method pattern
    defines an interface for
    creating an object, but lets
    subclasses decide which
    class to instantiate.
      The Factory Method defers
    instantiation

    View Slide

  57. Factory Method: considerations
    Simone Carletti, Altura Labs
      It’s difficult to find in PHP an original implementation of the Factory
    Method as defined by the GoF
      There are different variation to the original Factory Method
      The GoF discussed both Factory Method and Abstract Factory

    View Slide

  58. Factory Method: example
    Simone Carletti, Altura Labs
    public static function factory($uri = 'http')
    {
    // Separate the scheme from the scheme-specific parts
    $uri = explode(':', $uri, 2);
    $scheme = strtolower($uri[0]);
    $schemeSpecific = isset($uri[1]) === true ? $uri[1] : '';
    if (strlen($scheme) === 0) {
    require_once 'Zend/Uri/Exception.php';
    throw new Zend_Uri_Exception('An empty string was supplied for the scheme');
    }
    // Security check: $scheme is used to load a class file, so only alphanumerics are allowed.
    if (ctype_alnum($scheme) === false) {
    require_once 'Zend/Uri/Exception.php';
    throw new Zend_Uri_Exception('Illegal scheme supplied, only alphanumeric characters are
    permitted');
    }
    /**
    * Create a new Zend_Uri object for the $uri. If a subclass of Zend_Uri exists for the
    * scheme, return an instance of that class. Otherwise, a Zend_Uri_Exception is thrown.
    */
    switch ($scheme) { /* factory pattern implementation */ }
    Zend_Loader::loadClass($className);
    $schemeHandler = new $className($scheme, $schemeSpecific);
    return $schemeHandler;
    }

    View Slide

  59. Factory Method: example
    Simone Carletti, Altura Labs
    public static function factory($uri = 'http')
    {
    // ...
    switch ($scheme) {
    case 'http':
    // Break intentionally omitted
    case 'https':
    $className = 'Zend_Uri_Http';
    break;
    case 'mailto':
    // TODO
    default:
    require_once 'Zend/Uri/Exception.php';
    throw new Zend_Uri_Exception("Scheme \"$scheme\" is not supported");
    break;
    }
    Zend_Loader::loadClass($className);
    $schemeHandler = new $className($scheme, $schemeSpecific);
    return $schemeHandler;
    }

    View Slide

  60. Factory Method: in the wild
    Simone Carletti, Altura Labs
      Zend_Uri::factory() returns the right Zend_Uri subclass according
    to the type of URI passed as parameter
      Zend_Cache::factory() returns the best Zend_Cache engines
    according to given options and configurations
      Doctrine contains several implementation of the Factory pattern.
    For example Doctrine_Node::factory() return node instance based
    upon chosen implementation

    View Slide

  61. Factory Method: using & abusing
    Simone Carletti, Altura Labs
      Use the Factory Method only if you really need it
      Don’t anticipate future needs, refactoring is usually the right choice
      Plans might change and you just spent time building useless, heavy
    infrastructures
      Be Smart! Be Agile! Be Lazy!

    View Slide

  62. Lazy Initialization
    Lazy evaluation
    Simone Carletti, Altura Labs

    View Slide

  63. Lazy Initialization: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You want to delay the creation
    of an instance or an
    expensive operation until the
    first time it is needed
      You want the operation to be
    executed only when and if
    necessary
      The Lazy Initialization pattern
    delays the creation of an
    object or the execution of an
    expensive process until the
    first time it is needed

    View Slide

  64. Lazy Initialization: example
    Simone Carletti, Altura Labs
    class PluginManager {
    protected $_plugins;

    public function getPlugins() {

    // parse list once only when needed and store it in memory

    if (null === $this->_plugins) {

    $this->_plugins = $this->parsePluginList();

    }

    return $this->parsePluginList();

    }

    public function activate($class) {

    require_once $class; // require the class once 

    $plugin = new $class; // only if and when needed

    $plugin->activate();

    }

    public function deactivate($class) {

    if (!isActive($class)) {

    require_once 'Plugin_Not_Active_Exception.php';

    throw new PluginNotActiveException("Plugin $class not active");

    }

    $plugin = new $class;

    $plugin->deactivate();

    }

    // check whether $class is in getPlugins()

    protected function isActive($class) { /* */ }

    // scans a folder tree looking for plugin files

    // this is a really expensive operation

    protected function parsePluginList() { /* */ }

    }


    View Slide

  65. Lazy Initialization: in the wild
    Simone Carletti, Altura Labs
      The most part of Zend_Exception classes in Zend Framework are
    lazy-loaded
      Controller classes in Symfony, Zend Framework and (almost) every
    PHP Framework are lazy-loaded
      Many resources in Xoops, such as the ReplacementArray, are
    lazy-instantiated
      Drupal maintains an internal registry of functions or classes in the
    system, allowing it to lazy-load code files as needed

    View Slide

  66. Adapter
    Simone Carletti, Altura Labs

    View Slide

  67. Adapter: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You want to reuse a library in
    your application but it doesn’t
    match your interface
      You want to normalize
    multiple libraries to use the
    same interface
      The Adapter pattern converts
    the interface of a class into
    another interface clients
    expect
      Adapter lets classes work
    together that couldn’t
    otherwise because of
    incompatible interfaces

    View Slide

  68. Adapter: considerations
    Simone Carletti, Altura Labs
      Adapter is a common pattern when using third party libraries
      If you are developing your own libraries, you might want to avoid
    adapters and use common habits and guidelines
      Often using in cooperation with other patterns, such as the Strategy
    pattern
      Adapt or Modify?
      Adapter pattern and Proxy pattern are similar, but they have
    different intents. Adapter is meant to change the interface of an
    existing object

    View Slide

  69. Adapter: example
    Simone Carletti, Altura Labs
    class MySQL_Connection
    {
    public static function connect($host, $user, $pass)
    {
    // main connection logic
    }
    }
    class PostgreSQL
    {
    public static function do_connect($args = array())
    {
    // main connection logic
    }
    }
    class OracleCoolLibrary
    {
    public function __construct($args = array())
    {
    // main connection logic
    }
    public function connect()
    {
    // main connection logic
    }
    }

    View Slide

  70. Adapter: example
    Simone Carletti, Altura Labs
    class MySQL_Adapter extends MySQL_Connection
    {
    public static function connect($params)
    {
    $host, $user, $pass = list($params);
    parent::connect(array($host, $user, $pass));
    }
    }
    class PostgreSQL_Adapter extends PostgreSQL
    {
    public static function connect($params)
    {
    self::do_connect($params);
    }
    }
    class Oracle_Adapter extends OracleCoolLibrary
    {
    public static function connect($params)
    {
    $instance = new OracleCoolLibrary($params);
    $instance->connect();
    }
    }
    class MySQL_Connection
    {
    public static function connect($host, $user, $pass)
    {
    // main connection logic
    }
    }
    class PostgreSQL
    {
    public static function do_connect($args = array())
    {
    // main connection logic
    }
    }
    class OracleCoolLibrary
    {
    public function __construct($args = array())
    {
    // main connection logic
    }
    public function connect()
    {
    // main connection logic
    }
    }

    View Slide

  71. Adapter: in the wild
    Simone Carletti, Altura Labs
      Zend_Db and all the database-specific subclasses
      Zend_Http_Client provides different adapters for different purposes
    (including a test adapter)
      Doctrine DBAdapter and database-specific subclasses

    View Slide

  72. Adapter: using & abusing
    Simone Carletti, Altura Labs
      Avoid creating adapter of adapters
      Go to the main library and build the Adapter at the lowest possible
    level

    View Slide

  73. Proxy
    Simone Carletti, Altura Labs

    View Slide

  74. Proxy: problem & solution
    Problem
      You need to provide access to
    an object without exposing the
    object directly
      You want to control access to
    that object
      You want to filter access to
    that object
    Solution
      The Proxy pattern provides a
    surrogate or placeholder for
    an other object to control
    access to it
    Simone Carletti, Altura Labs

    View Slide

  75. Proxy: considerations
      The Proxy is build around a lie
      You might want to expose a similar public API
      Remember: composition over inheritance
      Duck Typing or Interface
      You might want to improve performance for expensive operations
    Simone Carletti, Altura Labs

    View Slide

  76. Proxy: example
    Simone Carletti, Altura Labs
    // This class represents a simple bank account
    class Account
    {
    protected $balance;
    public function __construct()
    {
    }
    public function deposit($amount)
    {
    $this->balance += (int) $amount;
    return $this;
    }
    public function withdraw($amount)
    {
    $this->balance -= (int) $amount;
    return $this;
    }
    public function getBalance()
    {
    return $this->balance;
    }
    }

    View Slide

  77. Proxy: example
    Simone Carletti, Altura Labs
    class AccountProxy
    {
    protected $account;
    public function __construct($account)
    {
    $this->account = $account;
    return $this;
    }
    public function deposit($amount)
    {
    $this->account->deposit($amount);
    return $this;
    }
    public function withdraw($amount)
    {
    $this->account->withdraw($amount);
    return $this;
    }
    public function getBalance()
    {
    return $this->account->getBalance();
    }
    }
    $proxy = new AccountProxy(new Account());
    $proxy->deposit(20)->withdraw(5);
    print $proxy->getBalance();

    View Slide

  78. class AccountProxy
    {
    protected $account;
    public function __construct($account)
    {
    $this->account = $account;
    return $this;
    }
    public function deposit($amount)
    {
    $this->account->deposit($amount);
    return $this;
    }
    public function withdraw($amount)
    {
    $this->account->withdraw($amount);
    return $this;
    }
    public function getBalance()
    {
    return $this->account->getBalance();
    }
    }
    Proxy: example with Lazy Instantiation pattern
    Simone Carletti, Altura Labs
    class AccountProxy
    {
    protected $account;
    public function deposit($amount)
    {
    $this->getAccount()->deposit($amount);
    return $this;
    }
    public function withdraw($amount)
    {
    $this->getAccount()->withdraw($amount);
    return $this;
    }
    public function getBalance()
    {
    return $this->getAccount()->getBalance();
    }
    protected function getAccount()
    {
    if (null === $this->account) {
    $this->account = new Account();
    }
    return $this->account;
    }
    }

    View Slide

  79. Proxy: Virtual Proxy
    Simone Carletti, Altura Labs
    class AccountProxy
    {
    protected $account;
    public function deposit($amount)
    {
    $this->getAccount()->deposit($amount);
    return $this;
    }
    public function withdraw($amount)
    {
    $this->getAccount()->withdraw($amount);
    return $this;
    }
    public function getBalance()
    {
    return $this->getAccount()->getBalance();
    }
    protected function getAccount()
    {
    if (null === $this->account) {
    $this->account = new Account();
    }
    return $this->account;
    }
    }
    account
    __call
    $args
    $this or $value
    class AccountProxy
    {
    protected $account;
    public function __call($name, $args)
    {
    $account = $this->getAccount();
    if (!is_callable(array(&$account, $name))) {
    throw new Exception("No method $name");
    }
    $params = array(&$account, $name) ;
    $return = call_user_func_array($params, $args);
    return $return === $account ? $this : $return;
    }
    protected function getAccount()
    {
    if (null === $this->account) {
    $this->account = new Account();
    }
    return $this->account;
    }
    }

    View Slide

  80. Proxy: in the wild
    Simone Carletti, Altura Labs
      PHP 5 SOAP Library has examples of Remote Proxy in the WSDL
    mechanism
      Many classes in Symfony provide proxy methods to shorten the
    code needed for get/set operations
      $request->getParameterHolder()->set('foo', 'bar');
      $request->setParameter('foo', 'bar');

    View Slide

  81. Proxy: using & abusing
    Simone Carletti, Altura Labs
      Don’t forget to redefine special class methods accordingly
      __clone
      __get
      __set
      __toString
      …
      Avoid responsibility-mistakes
      Be sure all classes are well documented, especially when using
    virtual proxies and difficult-to-auto-document features

    View Slide

  82. Iterator
    Simone Carletti, Altura Labs

    View Slide

  83. Iterator: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You have a complex
    aggregate object and you
    want to access its elements
    without working on
    implementation
      You want to traverse and
    manipulate a collection object
      The Iterator pattern provides a
    way to access the elements of
    an aggregate object
    sequentially without exposing
    the underlying representation

    View Slide

  84. Iterator: considerations
    Simone Carletti, Altura Labs
      You might not realize it, but you use the Iterator pattern every day
    working with Arrays

    View Slide

  85. Iterator: example
    Simone Carletti, Altura Labs
    $colors = array('yellow', 'orange', 'green');
    foreach($colors as $color) {
    print "Current color: $color\n";
    }
    $items = array(
    'first' => 1,
    'second' => 2,
    'third' => 3,
    );
    foreach($items as $key => $value) {
    print "Value $value for key $key\n";
    }

    View Slide

  86. Iterator: SPL example
    Simone Carletti, Altura Labs
    class Zend_Service_Amazon_ResultSet implements SeekableIterator
    {
    /**
    * A DOMNodeList of elements
    */
    protected $_results = null;
    /**
    * Current index for SeekableIterator
    */
    protected $_currentIndex = 0;
    /**
    * Implement SeekableIterator::current()
    *
    * @return Zend_Service_Amazon_Item
    */
    public function current()
    {
    return new Zend_Service_Amazon_Item($this->_results->item($this->_currentIndex));
    }
    /**
    * Implement SeekableIterator::key()
    *
    * @return int
    */
    public function key()
    {
    return $this->_currentIndex;
    }

    View Slide

  87. Iterator: SPL example
    Simone Carletti, Altura Labs
    /**
    * Implement SeekableIterator::next()
    */
    public function next() {
    $this->_currentIndex += 1;
    }
    /**
    * Implement SeekableIterator::rewind()
    */
    public function rewind() {
    $this->_currentIndex = 0;
    }
    /**
    * Implement SeekableIterator::seek()
    */
    public function seek($index) {
    $indexInt = (int) $index;
    if ($indexInt >= 0 && (null === $this->_results || $indexInt < $this->_results->length)) {
    $this->_currentIndex = $indexInt;
    } else {
    throw new OutOfBoundsException("Illegal index '$index'");
    }
    }
    /**
    * Implement SeekableIterator::valid()
    */
    public function valid() {
    return null !== $this->_results && $this->_currentIndex < $this->_results->length;
    }
    }

    View Slide

  88. Iterator: PHP 5 SPL
    Simone Carletti, Altura Labs
      SPL offers some advanced Iterator algorithms
      Iterator
      RecursiveIterator
      SeekableIterator
      ArrayIterato
      Don’t forget to have a look at the Countable interface

    View Slide

  89. Iterator: in the wild
    Simone Carletti, Altura Labs
      PHP 5 DirectoryIterator library
      Zend_Feed, Zend_Service_Amazon, Zend_Service_Technorati in
    the Zend Framework

    View Slide

  90. Iterator: using & abusing
    Simone Carletti, Altura Labs
      Limit (or avoid) using iterators for changing internal object status
      Don’t fall into the concurrent modification trap!
      Be sure you are not altering iterator internal index

    View Slide

  91. Observer
    Simone Carletti, Altura Labs

    View Slide

  92. Observer: OMG!
    Simone Carletti, Altura Labs
    class User {
    protected $_logger;
    protected $_username;
    public function __construct($username, $notifier)
    {
    $this->_username = $username;
    $this->_logger = new Logger();
    $this->_notifier = $notifier;
    }
    public function setUsername($username)
    {
    $this->_username = $username;
    $this->_logger->debug("$username changed username");
    $this->_notifier->userChangedLogin($this);
    }
    public function login($password)
    {
    // login login
    $this->_logger->debug("$username logged in");
    }
    }

    View Slide

  93. Observer: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You want objects to interact
    each other without making the
    classes tightly coupled
      You need to maintain
    consistency between related
    objects with data integrity in
    mind
      The Observer pattern defines
    dependency between objects
    so that when one object
    change state, all its
    dependents are notified and
    updated automatically

    View Slide

  94. Observer: implementation
    Simone Carletti, Altura Labs
    class Subject
    {

    protected $_observers = array();

    public function attach($observer)

    {

    $this->_objservers[] = $observer;

    }

    public function detach($observer)

    { 

    $observers = array();

    foreach($this->_observers as $object) {

    if ($object !== $observer) {

    $observers[] = $object;

    }

    }

    $this->_observers = $observers;

    }

    public function notify()

    {

    foreach($this->_observers as $observer) {

    $observer->update($this);

    }

    }

    }

    View Slide

  95. Observer: example
    Simone Carletti, Altura Labs
    class User {
    protected $_username;
    protected $_observers = array();
    public function attach($observer)
    {
    // ... see implementation
    }
    public function detach($observer)
    {
    // ... see implementation
    }
    public function notify()
    {
    // ... see implementation
    }
    public function setUsername($username)
    {
    $this->_username = $username;
    $this->notify();
    }
    }
    $user = new User();
    $user->attach(new Logger());
    $user->attach(Notifier::getInstance());
    $user->setUsername('foobar');

    View Slide

  96. Observer: PHP 5 SPL
    Simone Carletti, Altura Labs
      SPL suggests a standard way of implementing the Observer
    pattern.
      interface SplObserver
      interface SplSubject
      class SplObjectStorage

    View Slide

  97. Observer: in the wild
    Simone Carletti, Altura Labs
      Zend_XmlRpc_Server_Fault in Zend Framework accepts a list of
    observers and notifies them in case of unexpected behaviors

    View Slide

  98. Observer: using & abusing
    Simone Carletti, Altura Labs
      Don’t notify observers if you don’t need
      Consider to add specific notifications if you find yourself calling
    update() too many times for different kind of notifications
      ->update()
      ->save()
      ->delete()
      Be careful to notify observers only when a consistent change is
    complete.
      Remember, your object should provide a way to let observers know
    what changed

    View Slide

  99. Template Method
    Simone Carletti, Altura Labs

    View Slide

  100. Template Method: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      The Template Method pattern
    describes the skeleton of an
    algorithm in an operation,
    deferring some steps to
    subclasses
      Template Method lets
    subclasses redefine certain
    steps of an algorithm without
    changing the algorithm’s
    structure
      You have a complex come
    that might vary somewhere in
    the middle
      You want to let subclasses
    change part of the abstract
    class algorithms
      You want to defer to
    implementations some parts
    of an algorithms

    View Slide

  101. Template Method: considerations
    Simone Carletti, Altura Labs
      Template Method is based on inheritance
      Strategy pattern can be considered the composition variant

    View Slide

  102. Template Method: implementation
    Simone Carletti, Altura Labs
    abstract class AbstractClass
    {
    public final function templateMethod()
    {
    print "AbstractClass::templateMethod()\n";
    $this->mandatoryMethod();
    $this->optionalMethod();
    }
    protected abstract function mandatoryMethod();
    protected function optionalMethod()
    {
    print "AbstractClass::optionalMethod()\n";
    }
    }
    class FirstConcreteClass extends AbstractClass
    {
    protected function mandatoryMethod() {
    print "FirstConcreteClass::mandatoryMethod()\n";
    }
    }
    class SecondConcreteClass extends AbstractClass
    {
    protected function mandatoryMethod() {
    print "SecondConcreteClass::mandatoryMethod()\n";
    }
    protected function optionalMethod() {
    print "SecondConcreteClass::optionalMethod()\n";
    }
    }
    $o = new FirstConcreteClass;
    $o->templateMethod();
    # AbstractClass::templateMethod()
    # FirstConcreteClass::mandatoryMethod()
    # AbstractClass::optionalMethod()
    $o = new SecondConcreteClass;
    $o->templateMethod();
    # AbstractClass::templateMethod()
    # SecondConcreteClass::mandatoryMethod()
    # SecondConcreteClass::optionalMethod()

    View Slide

  103. Template Method: example
    Simone Carletti, Altura Labs
    abstract class Page
    {
    protected $_title;
    protected $_content;
    public function __construct($title, $content) {
    $this->_title = $title;
    $this->_content = (array) $content;
    }
    public final function generate() {
    $this->generateHeader();
    $this->generateTitle();
    $this->generateBodyHeader();
    $this->generateBody();
    $this->generateBodyFooter();
    $this->generateFooter();
    }
    protected abstract function generateHeader();
    protected abstract function generateBodyHeader();
    protected abstract function generateBodyFooter();
    protected abstract function generateFooter();
    protected abstract function generateLine($line);
    protected function generateTitle() {
    printf("Title: %s\n", $this->_title);
    }
    protected final function generateBody() {
    foreach($this->_content as $line) {
    $this->generateLine($line);
    }
    }
    }

    View Slide

  104. Template Method: example
    Simone Carletti, Altura Labs
    class HtmlPage extends Page {
    protected function generateHeader() {
    printf("\n");
    }
    protected function generateTitle() {
    printf("%s\n",
    $this->_title);
    }
    protected function generateBodyHeader() {
    printf("\n");
    }
    protected function generateBodyFooter() {
    printf("\n");
    }
    protected function generateFooter() {
    printf("\n\n");
    }
    protected function generateLine($line) {
    printf($line);
    }
    }
    class FeedPage extends Page {
    protected function generateHeader() {
    printf("\n");
    printf("\n");
    printf(" \n");
    }
    protected function generateTitle() {
    $title = $this->_title;
    printf(" %s\n", $title);
    }
    protected function generateBodyHeader() {
    }
    protected function generateBodyFooter() {
    }
    protected function generateFooter() {
    printf(" \n");
    printf("\n\n");
    }
    protected function generateLine($line) {
    printf(" %s\n",
    $line);
    }
    }

    View Slide

  105. Template Method: example
    Simone Carletti, Altura Labs
    $t = 'This is the title';
    $c = array('First entry', 'Second entry');
    $o = new FeedPage($t, $c);
    $o->generate();
    $o = new HtmlPage($t, $c);
    $o->generate();



    This is the title
    First entry
    Second entry



    This is the title

    First entrySecond entry

    View Slide

  106. Template Method: in the wild
    Simone Carletti, Altura Labs
      Propel bases the full public API on a custom implementation of the
    Template Method pattern.
      BaseClass is the “abstract” class
      Class extends BaseClass and overwrites only those methods you want to
    customize

    View Slide

  107. Template Method: using & abusing
    Simone Carletti, Altura Labs
      Avoid creating abstract classes that forces concrete classes to
    implement tons of methods

    View Slide

  108. Strategy
    Simone Carletti, Altura Labs

    View Slide

  109. Strategy: problem & solution
    Problem Solution
    Simone Carletti, Altura Labs
      You have a complex operation
    that might vary algorithms at
    runtime and you want to
    encapsulate algorithms
      You want to be able to easily
    test the algorithms
      You want to be able to add,
    remove or change an
    algorithm without changing
    the global operation logic
      The Strategy pattern defines a
    family of algorithms,
    encapsulate each one, and
    make them interchangeable
      Strategy lets the algorithm
    vary independently from
    clients that use it, often at
    runtime level

    View Slide

  110. Strategy: considerations
    Simone Carletti, Altura Labs
      Strategy is an excellent example of composition and delegation
      Strategy is an alternative to subclassing
      Strategy and Template Method patterns expose different approach
    to a similar problem
      Duck Typing is a common practice in Strategy pattern

    View Slide

  111. Strategy: example
    Simone Carletti, Altura Labs
    interface Sorter {
    public function sort();
    }
    class SelectionSort implements Sorter
    {
    public function sort($array)
    {
    // selection sort algorithm
    }
    }
    class InsertionSort implements Sorter
    {
    public function sort($array)
    {
    // insertion sort algorithm
    }
    }
    class SuperSecretSort implements Sorter
    {
    public function sort($array)
    {
    // insertion sort algorithm
    }
    }
    class ArraySorter
    {
    protected $_a;
    public function __construct($a)
    {
    $this->_a;
    return self;
    }
    public function sortWithAlgorithm($algorithm)
    {
    return $algorithm->sort($this->_a);
    }
    }
    $array = array('white', 'green', 'black', 'red');
    $sorter = new ArraySorter($a);
    $sorter->sortWithAlgorithm(new SelectionSort());
    $sorter->sortWithAlgorithm(new InsertionSort());

    View Slide

  112. Strategy: example with Lazy Instantiation
    Simone Carletti, Altura Labs
    class ArraySorter
    {
    protected $_a;
    public function __construct($a)
    {
    $this->_a;
    return self;
    }
    public function sortWithAlgorithm($algorithm)
    {
    require_once $algorithm;
    $sorter = new $algorithm();
    return $sorter->sort($this->_a);
    }
    }
    $array = array('white', 'green', 'black', 'red');
    $sorter = new ArraySorter($a);
    $sorter->sortWithAlgorithm('SelectionSort');
    $sorter->sortWithAlgorithm('InsertionSort');

    View Slide

  113. Strategy: in the wild
    Simone Carletti, Altura Labs
      Zend_Pdf_Resource_Image_Png allows different image
    compression strategies (work in progress)
      Symfony enables you to configure different escaping strategies
    with the escaping_strategy variable in your configuration file

    View Slide

  114. Strategy: using & abusing
    Simone Carletti, Altura Labs
      Make sure you are not coupling the context with a specific strategy
      Be careful when using one strategy as the default one

    View Slide

  115. Time is running out…
    Simone Carletti, Altura Labs
    … but there are many other interesting Design Patterns out of there.
      Registry
      Mock Object
      Command
      Decorator
      Chain or Responsibility
      Data Mapper
      Active Record
      Table Data Gateway and Row Data Gateway

    View Slide

  116. Beyond this Presentation
    Simone Carletti, Altura Labs

    View Slide

  117. Literature
    Simone Carletti, Altura Labs

    View Slide

  118. Readings
      http://www.fluffycat.com/PHP-Design-Patterns/
      http://www.phppatterns.com/
      http://www.devarticles.com/c/a/PHP/Introduction-to-Design-
    Patterns-Using-PHP/
      http://www.ibm.com/developerworks/library/os-php-designptrns/
      http://www.phplibrairies.com/tutorial_design-pattern_en.html
    Simone Carletti, Altura Labs

    View Slide

  119. Q & A
    (part 2/2)
    Simone Carletti, Altura Labs

    View Slide

  120. Thank you!
    [email protected]
    www.simonecarletti.com
    Slides will be available at
    www.slideshare.net/weppos
    Simone Carletti, Altura Labs

    View Slide