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

Design Patterns in PHP (PHPCon Italia)

Embed Size (px)

DESCRIPTION

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.

Citation preview

Page 1: Design Patterns in PHP (PHPCon Italia)

Simone Carletti, Altura Labs [email protected]

Design Patterns in PHP

Page 2: Design Patterns in PHP (PHPCon Italia)

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

Page 3: Design Patterns in PHP (PHPCon Italia)

.. and what about you?

Simone Carletti, Altura Labs

Page 4: Design Patterns in PHP (PHPCon Italia)

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

Page 5: Design Patterns in PHP (PHPCon Italia)

Designing object oriented software is hard, and designing reusable object-oriented software is even harder.

Design Patters (GoF)

Simone Carletti, Altura Labs

Page 6: Design Patterns in PHP (PHPCon Italia)

Design Patterns

Simone Carletti, Altura Labs

Page 7: Design Patterns in PHP (PHPCon Italia)

Design Patterns describe simple and elegant solutions to specific problems in

object oriented software design.

Simone Carletti, Altura Labs

Page 8: Design Patterns in PHP (PHPCon Italia)

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.

Page 9: Design Patterns in PHP (PHPCon Italia)

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.

Page 10: Design Patterns in PHP (PHPCon Italia)

The Gang of Four

Simone Carletti, Altura Labs

Page 11: Design Patterns in PHP (PHPCon Italia)

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

Page 12: Design Patterns in PHP (PHPCon Italia)

What are Design Patterns

  Solution to common development problems

  Reusable successful design and architectures

  Common language for developers

Simone Carletti, Altura Labs

Page 13: Design Patterns in PHP (PHPCon Italia)

What Design Patterns are not

  The panacea for solving all development problems

  Ready to use code scripts

  Cut & Paste solutions

Simone Carletti, Altura Labs

Page 14: Design Patterns in PHP (PHPCon Italia)

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

Page 15: Design Patterns in PHP (PHPCon Italia)

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)

Page 16: Design Patterns in PHP (PHPCon Italia)

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

Page 17: Design Patterns in PHP (PHPCon Italia)

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

Page 18: Design Patterns in PHP (PHPCon Italia)

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

Page 19: Design Patterns in PHP (PHPCon Italia)

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

Page 20: Design Patterns in PHP (PHPCon Italia)

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

Page 21: Design Patterns in PHP (PHPCon Italia)

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!

Page 22: Design Patterns in PHP (PHPCon Italia)

Design for Changes

Do you remember? But we careful…

Simone Carletti, Altura Labs

Page 23: Design Patterns in PHP (PHPCon Italia)

Avoid Over-Engineering

Simone Carletti, Altura Labs

Page 24: Design Patterns in PHP (PHPCon Italia)

Avoid Under-Engineering

Simone Carletti, Altura Labs

Page 25: Design Patterns in PHP (PHPCon Italia)

Don’t fall into the “everything is a pattern” trap

Simone Carletti, Altura Labs

Page 26: Design Patterns in PHP (PHPCon Italia)

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

Page 27: Design Patterns in PHP (PHPCon Italia)

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

Page 28: Design Patterns in PHP (PHPCon Italia)

Design Pattern Classification

Simone Carletti, Altura Labs

Page 29: Design Patterns in PHP (PHPCon Italia)

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

Page 30: Design Patterns in PHP (PHPCon Italia)

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

Page 31: Design Patterns in PHP (PHPCon Italia)

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

Page 32: Design Patterns in PHP (PHPCon Italia)

Q & A (part 1/2)

Simone Carletti, Altura Labs

Page 33: Design Patterns in PHP (PHPCon Italia)

Design Patterns in Action

Simone Carletti, Altura Labs

Page 34: Design Patterns in PHP (PHPCon Italia)

Pattern Template

  Problem & Solution

  Considerations

  In pictures

  Implementation & Examples

  In the Wild

  Using and Abusing

Simone Carletti, Altura Labs

Page 35: Design Patterns in PHP (PHPCon Italia)

MVC

Simone Carletti, Altura Labs

Page 36: Design Patterns in PHP (PHPCon Italia)

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

Page 37: Design Patterns in PHP (PHPCon Italia)

MVC: in pictures

Simone Carletti, Altura Labs

http://betterexplained.com/articles/intermediate-rails-understanding-models-views-and-controllers/

Page 38: Design Patterns in PHP (PHPCon Italia)

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.

Page 39: Design Patterns in PHP (PHPCon Italia)

MVC: decoupling View and Model

Simone Carletti, Altura Labs

Articles

Article::find()

XML Feed iPhone (X)HTML

$articles

$articles $articles

Page 40: Design Patterns in PHP (PHPCon Italia)

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

Page 41: Design Patterns in PHP (PHPCon Italia)

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

Page 42: Design Patterns in PHP (PHPCon Italia)

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'); }}

Page 43: Design Patterns in PHP (PHPCon Italia)

MVC: find the error (1/solution)

Simone Carletti, Altura Labs

// BookController represents the Book controllerclass BookController{ public function SuggestionsAction() { $this->choices = Book::findSuggestionsForKeyword($params['query']); $this->render('suggestions'); }}

// Book class represent the Book modelclass 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); }}

Page 44: Design Patterns in PHP (PHPCon Italia)

MVC: find the error (2)

Simone Carletti, Altura Labs

<h2>Available Tickets</h2>

<?php$tickets = Ticket::findAll(array('conditions' => array('published' => true)));

$available = array();foreach($tickets as $ticket) { if ($currentUser->isAdmin() == true || $ticket->isPublic()) { $available[] = $ticket; }}

?>

<ul><?php foreach($available as $ticket) { ?> <li><?php echo $ticket->title == '' ? 'This ticket has no title' : $ticket->title . ' ' . $ticket->price; ?></li><?php } ?></ul>

Page 45: Design Patterns in PHP (PHPCon Italia)

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 viewsclass Ticket { // ...

public function getFormattedTitle() { return $ticket->title == '' ? 'This ticket has no title' : $ticket->title . ' ' . $ticket->price; }}

// or delegate the responsibility to the view using helpersclass TicketsHelper{ public static function formatTitle($ticket) { return $ticket->title == '' ? 'This ticket has no title' : $ticket->title . ' ' . $ticket->price; }}

<h2>Available Tickets</h2>

<ul><?php foreach($tickets as $ticket) { ?> <li> <?php $ticket->getFormattedTitle(); ?> </li><?php } ?></ul>

<!-- or using helpers -->

<h2>Available Tickets</h2>

<ul><?php foreach($tickets as $ticket) { ?> <li> <?php TicketsHelper::formatTitle($ticket) ?> </li><?php } ?></ul>

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

Controller

View

Model

Helper

Page 46: Design Patterns in PHP (PHPCon Italia)

Singleton

The Highlander

Simone Carletti, Altura Labs

Page 47: Design Patterns in PHP (PHPCon Italia)

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

Page 48: Design Patterns in PHP (PHPCon Italia)

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

Page 49: Design Patterns in PHP (PHPCon Italia)

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; }}

Page 50: Design Patterns in PHP (PHPCon Italia)

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();

Page 51: Design Patterns in PHP (PHPCon Italia)

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

Page 52: Design Patterns in PHP (PHPCon Italia)

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

Page 53: Design Patterns in PHP (PHPCon Italia)

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

Page 54: Design Patterns in PHP (PHPCon Italia)

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

Page 55: Design Patterns in PHP (PHPCon Italia)

Factory Method

Simone Carletti, Altura Labs

Page 56: Design Patterns in PHP (PHPCon Italia)

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

Page 57: Design Patterns in PHP (PHPCon Italia)

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

Page 58: Design Patterns in PHP (PHPCon Italia)

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;}

Page 59: Design Patterns in PHP (PHPCon Italia)

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;}

Page 60: Design Patterns in PHP (PHPCon Italia)

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

Page 61: Design Patterns in PHP (PHPCon Italia)

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!

Page 62: Design Patterns in PHP (PHPCon Italia)

Lazy Initialization

Lazy evaluation

Simone Carletti, Altura Labs

Page 63: Design Patterns in PHP (PHPCon Italia)

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

Page 64: Design Patterns in PHP (PHPCon Italia)

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() { /* */ } }

Page 65: Design Patterns in PHP (PHPCon Italia)

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

Page 66: Design Patterns in PHP (PHPCon Italia)

Adapter

Simone Carletti, Altura Labs

Page 67: Design Patterns in PHP (PHPCon Italia)

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

Page 68: Design Patterns in PHP (PHPCon Italia)

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

Page 69: Design Patterns in PHP (PHPCon Italia)

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 }}

Page 70: Design Patterns in PHP (PHPCon Italia)

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 }}

Page 71: Design Patterns in PHP (PHPCon Italia)

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

Page 72: Design Patterns in PHP (PHPCon Italia)

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

Page 73: Design Patterns in PHP (PHPCon Italia)

Proxy

Simone Carletti, Altura Labs

Page 74: Design Patterns in PHP (PHPCon Italia)

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

Page 75: Design Patterns in PHP (PHPCon Italia)

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

Page 76: Design Patterns in PHP (PHPCon Italia)

Proxy: example

Simone Carletti, Altura Labs

// This class represents a simple bank accountclass 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; }}

Page 77: Design Patterns in PHP (PHPCon Italia)

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();

Page 78: Design Patterns in PHP (PHPCon Italia)

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; }}

Page 79: Design Patterns in PHP (PHPCon Italia)

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; }}

Page 80: Design Patterns in PHP (PHPCon Italia)

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');

Page 81: Design Patterns in PHP (PHPCon Italia)

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

Page 82: Design Patterns in PHP (PHPCon Italia)

Iterator

Simone Carletti, Altura Labs

Page 83: Design Patterns in PHP (PHPCon Italia)

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

Page 84: Design Patterns in PHP (PHPCon Italia)

Iterator: considerations

Simone Carletti, Altura Labs

  You might not realize it, but you use the Iterator pattern every day working with Arrays

Page 85: Design Patterns in PHP (PHPCon Italia)

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";}

Page 86: Design Patterns in PHP (PHPCon Italia)

Iterator: SPL example

Simone Carletti, Altura Labs

class Zend_Service_Amazon_ResultSet implements SeekableIterator{ /** * A DOMNodeList of <Item> 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; }

Page 87: Design Patterns in PHP (PHPCon Italia)

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; }}

Page 88: Design Patterns in PHP (PHPCon Italia)

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

Page 89: Design Patterns in PHP (PHPCon Italia)

Iterator: in the wild

Simone Carletti, Altura Labs

  PHP 5 DirectoryIterator library

  Zend_Feed, Zend_Service_Amazon, Zend_Service_Technorati in the Zend Framework

Page 90: Design Patterns in PHP (PHPCon Italia)

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

Page 91: Design Patterns in PHP (PHPCon Italia)

Observer

Simone Carletti, Altura Labs

Page 92: Design Patterns in PHP (PHPCon Italia)

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"); }}

Page 93: Design Patterns in PHP (PHPCon Italia)

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

Page 94: Design Patterns in PHP (PHPCon Italia)

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); } }}

Page 95: Design Patterns in PHP (PHPCon Italia)

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');

Page 96: Design Patterns in PHP (PHPCon Italia)

Observer: PHP 5 SPL

Simone Carletti, Altura Labs

  SPL suggests a standard way of implementing the Observer pattern.   interface SplObserver   interface SplSubject   class SplObjectStorage

Page 97: Design Patterns in PHP (PHPCon Italia)

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

Page 98: Design Patterns in PHP (PHPCon Italia)

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

Page 99: Design Patterns in PHP (PHPCon Italia)

Template Method

Simone Carletti, Altura Labs

Page 100: Design Patterns in PHP (PHPCon Italia)

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

Page 101: Design Patterns in PHP (PHPCon Italia)

Template Method: considerations

Simone Carletti, Altura Labs

  Template Method is based on inheritance

  Strategy pattern can be considered the composition variant

Page 102: Design Patterns in PHP (PHPCon Italia)

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()

Page 103: Design Patterns in PHP (PHPCon Italia)

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); } }}

Page 104: Design Patterns in PHP (PHPCon Italia)

Template Method: example

Simone Carletti, Altura Labs

class HtmlPage extends Page { protected function generateHeader() { printf("<html>\n"); } protected function generateTitle() { printf("<head><title>%s</title></head>\n", $this->_title); } protected function generateBodyHeader() { printf("<body>\n"); } protected function generateBodyFooter() { printf("</body>\n"); } protected function generateFooter() { printf("</html>\n\n"); } protected function generateLine($line) { printf($line); }}

class FeedPage extends Page { protected function generateHeader() { printf("<?xml version=\"1.0\"?>\n"); printf("<rss version=\"2.0\">\n"); printf(" <channel>\n"); } protected function generateTitle() { $title = $this->_title; printf(" <title>%s</title>\n", $title); } protected function generateBodyHeader() { } protected function generateBodyFooter() { } protected function generateFooter() { printf(" </rss>\n"); printf("</channel>\n\n"); } protected function generateLine($line) { printf(" <item><title>%s</title></item>\n", $line); }}

Page 105: Design Patterns in PHP (PHPCon Italia)

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();

<?xml version="1.0"?><rss version="2.0"> <channel> <title>This is the title</title> <item><title>First entry</title></item> <item><title>Second entry</title></item> </rss></channel>

<html><head><title>This is the title</title></head><body>First entrySecond entry</body></html>

Page 106: Design Patterns in PHP (PHPCon Italia)

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

Page 107: Design Patterns in PHP (PHPCon Italia)

Template Method: using & abusing

Simone Carletti, Altura Labs

  Avoid creating abstract classes that forces concrete classes to implement tons of methods

Page 108: Design Patterns in PHP (PHPCon Italia)

Strategy

Simone Carletti, Altura Labs

Page 109: Design Patterns in PHP (PHPCon Italia)

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

Page 110: Design Patterns in PHP (PHPCon Italia)

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

Page 111: Design Patterns in PHP (PHPCon Italia)

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());

Page 112: Design Patterns in PHP (PHPCon Italia)

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');

Page 113: Design Patterns in PHP (PHPCon Italia)

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

Page 114: Design Patterns in PHP (PHPCon Italia)

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

Page 115: Design Patterns in PHP (PHPCon Italia)

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

Page 116: Design Patterns in PHP (PHPCon Italia)

Beyond this Presentation

Simone Carletti, Altura Labs

Page 117: Design Patterns in PHP (PHPCon Italia)

Literature

Simone Carletti, Altura Labs

Page 118: Design Patterns in PHP (PHPCon Italia)

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

Page 119: Design Patterns in PHP (PHPCon Italia)

Q & A (part 2/2)

Simone Carletti, Altura Labs

Page 120: Design Patterns in PHP (PHPCon Italia)

Thank you!

[email protected] www.simonecarletti.com

Slides will be available at www.slideshare.net/weppos

Simone Carletti, Altura Labs