Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Design Patterns Simone Carletti, Altura Labs

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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.

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

The Gang of Four Simone Carletti, Altura Labs

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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)

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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!

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Avoid Over-Engineering Simone Carletti, Altura Labs

Slide 24

Slide 24 text

Avoid Under-Engineering Simone Carletti, Altura Labs

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Design Pattern Classification Simone Carletti, Altura Labs

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Design Patterns in Action Simone Carletti, Altura Labs

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

MVC Simone Carletti, Altura Labs

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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.

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

MVC: find the error (2) Simone Carletti, Altura Labs

Available Tickets

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Singleton The Highlander Simone Carletti, Altura Labs

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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


Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

Factory Method Simone Carletti, Altura Labs

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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!

Slide 62

Slide 62 text

Lazy Initialization Lazy evaluation Simone Carletti, Altura Labs

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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


Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

Adapter Simone Carletti, Altura Labs

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Proxy Simone Carletti, Altura Labs

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

Iterator Simone Carletti, Altura Labs

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

Observer Simone Carletti, Altura Labs

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

Template Method Simone Carletti, Altura Labs

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Strategy Simone Carletti, Altura Labs

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

Beyond this Presentation Simone Carletti, Altura Labs

Slide 117

Slide 117 text

Literature Simone Carletti, Altura Labs

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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