Slide 1

Slide 1 text

Design Patterns Bootcamp Ralph Schindler, Matthew Weier O'Phinney, and Enrico Zimuel ZendCon 2012

Slide 2

Slide 2 text

What are Design Patterns?

Slide 3

Slide 3 text

“A formal way of documenting a solution to a design problem in a particular field of expertise. (http://en.wikipedia.org /wiki/Design_patterns)”

Slide 4

Slide 4 text

Elements of design patterns Describe the problem 1. Describe the solution 2. Describe when it is applicable 3.

Slide 5

Slide 5 text

A Grammar for Software Development

Slide 6

Slide 6 text

What design patterns are not Copy and paste solutions Standard implementation

Slide 7

Slide 7 text

What we'll cover today Foundation patterns Behavioral patterns Modeling patterns

Slide 8

Slide 8 text

Our presentation pattern Outline the problem Name the pattern Describe how the pattern implementation resolves the problem

Slide 9

Slide 9 text

Foundation patterns

Slide 10

Slide 10 text

Patterns we'll cover Bridge Facade Proxy Iterator Visitor Decorator

Slide 11

Slide 11 text

Bridge Problem: manage an abstraction with different implementations First solution: use inheritance to build different implementations Cons: the implementations are too close with the abstraction. The abstraction and implementations cannot be independently extended or composed. Better solution: use the Bridge pattern

Slide 12

Slide 12 text

Bridge: UML diagram

Slide 13

Slide 13 text

Implementation 1 interface DrawingAPI { 2 public function drawCircle($x, $y, $radius); 3 } 4 5 class DrawingAPI1 implements DrawingAPI { 6 7 public function drawCircle($x, $y, $radius) { 8 printf ("API1 draw (%d, %d, %d)\n", $x, $y, $radius); 9 } 10 } 11 12 class DrawingAPI2 implements DrawingAPI { 13 14 public function drawCircle($x, $y, $radius) { 15 printf ("API2 draw (%d, %d, %d)\n", $x, $y, $radius); 16 } 17 }

Slide 14

Slide 14 text

Implementation (2) 1 abstract class Shape { 2 protected $api; 3 protected $x; 4 protected $y; 5 public function __construct(DrawingAPI $api) { 6 $this->api = $api; 7 } 8 } 9 10 class CircleShape extends Shape { 11 protected $radius; 12 public function __construct($x, $y, $radius, DrawingAPI $api) { 13 parent::__construct($api); 14 $this->x = $x; 15 $this->y = $y; 16 $this->radius = $radius; 17 } 18 public function draw() { 19 $this->api->drawCircle($this->x, $this->y, $this->radius); 20 } 21 }

Slide 15

Slide 15 text

Usage example 1 $shapes = array( 2 new CircleShape(1, 3, 7, new DrawingAPI1()), 3 new CircleShape(5, 7, 11, new DrawingAPI2()), 4 ); 5 6 foreach ($shapes as $sh) { 7 $sh->draw(); 8 } 9 10 // Expected output: 11 // API1 draw (1, 3, 7) 12 // API2 draw (5, 7, 11)

Slide 16

Slide 16 text

Facade Problem: simplify the usage of a complex code Solution: use the Facade pattern, a simplified interface to a larger body of code, such as a class library. Using a facade schema we can hide all the logic of the complex code, while the mechanism in question knows nothing about the calling class.

Slide 17

Slide 17 text

Implementation 1 class CPU { 2 public function freeze() { 3 echo "Freeze the CPU\n"; 4 } 5 public function jump($address) { 6 echo "Jump to $address\n"; 7 } 8 public function execute() { 9 echo "Execute\n"; 10 } 11 } 12 class Memory { 13 public function load($address, $data) { 14 echo "Loading address $address with data: $data\n"; 15 } 16 } 17 class Disk { 18 public function read($sector, $size) { 19 return "data from sector $sector ($size)"; 20 } 21 }

Slide 18

Slide 18 text

Implementation (2) 1 class Computer { 2 const BOOT_ADDRESS = 0; 3 const BOOT_SECTOR = 1; 4 const SECTOR_SIZE = 16; 5 protected $cpu; 6 protected $mem; 7 protected $hd; 8 public function __construct(CPU $cpu, Memory $mem, Disk $hd) { 9 $this->cpu = $cpu; 10 $this->mem = $mem; 11 $this->hd = $hd; 12 } 13 public function startComputer() { 14 $this->cpu->freeze(); 15 $this->mem->load( 16 self::BOOT_ADDRESS, 17 $this->hd->read(self::BOOT_SECTOR, self::SECTOR_SIZE)); 18 $this->cpu->jump(self::BOOT_ADDRESS); 19 $this->cpu->execute(); 20 } 21 }

Slide 19

Slide 19 text

Proxy Problem 1: manage "expensive to create" objects, lazy-loading them only on first access Problem 2: provide a local object representation of remote system processes Problem 3: consuming and controlling access to another object Solution: Design a proxy class that access/extend the object, overriding one or more methods

Slide 20

Slide 20 text

Proxy: UML diagram

Slide 21

Slide 21 text

Implementation (Prob. 1) 1 interface ImageInterface 2 { 3 public function display(); 4 } 5 class Image implements ImageInterface 6 { 7 protected $filename; 8 public function __construct($filename) { 9 $this->filename = $filename; 10 $this->loadFromDisk(); 11 } 12 protected function loadFromDisk() { 13 echo "Loading {$this->filename}\n"; 14 } 15 public function display() { 16 echo "Display {$this->filename}\n"; 17 } 18 }

Slide 22

Slide 22 text

Implementation (Prob. 1) 1 class ProxyImage implements ImageInterface 2 { 3 protected $id; 4 protected $image; 5 public function __construct($filename) { 6 $this->filename = $filename; 7 } 8 public function display() { 9 if (null === $this->image) { 10 $this->image = new Image($this->filename); 11 } 12 return $this->image->display(); 13 } 14 }

Slide 23

Slide 23 text

Usage example 1 $filename = 'test.png'; 2 3 $image1 = new Image($filename); // loading necessary 4 echo $image1->display(); // loading unnecessary 5 6 $image2 = new ProxyImage($filename); // loading unnecessary 7 echo $image2->display(); // loading necessary 8 echo $image2->display(); // loading unnecessary 9 10 // Expected output: 11 // Loading test.png 12 // Display test.png 13 // Loading test.png 14 // Display test.png 15 // Display test.png

Slide 24

Slide 24 text

Implementation (Prob. 3) 1 class SomeObject 2 { 3 protected $message; 4 public function __construct($message) { 5 $this->message = $message; 6 } 7 protected function doSomething() { 8 return $this->message; 9 } 10 } 11 class Proxy extends SomeObject 12 { 13 protected $proxied; 14 public function __construct(SomeObject $o) { 15 $this->proxied = $o; 16 } 17 public function doSomething() { 18 return ucwords( 19 $this->proxied->doSomething() 20 ); 21 } 22 }

Slide 25

Slide 25 text

Usage example 1 $o = new SomeObject('foo bar'); 2 $p = new Proxy($o); 3 printf( 4 "Message from Proxy: %s\n", 5 $p->doSomething() 6 ); 7 8 // Expected output: 9 // Message from Proxy: Foo Bar

Slide 26

Slide 26 text

Iterator Problem: manipulate/traverse a collection of objects with a standard interface Solution: use the Iterator pattern that enables to traverse a container of objects PHP: PHP supports a standard Iterator interface (and Iterators classes in the SPL ready to be used)

Slide 27

Slide 27 text

PHP Iterator interface 1 interface Traversable { 2 } 3 4 interface Iterator extends Traversable { 5 public function current(); 6 public function key(); 7 public function next(); 8 public function rewind(); 9 public function valid(); 10 }

Slide 28

Slide 28 text

Implementation 1 class Fibonacci implements Iterator { 2 protected $value = 0; 3 protected $sum = 0; 4 protected $key = 0; 5 public function rewind() { 6 $this->value = 0; 7 $this->key = 0; 8 } 9 public function current() { 10 return $this->value; 11 } 12 public function key() { 13 return $this->key; 14 } 15 ...

Slide 29

Slide 29 text

Implementation (2) 1 ... 2 public function next() { 3 if ($this->value === 0) { 4 $this->value = 1; 5 } else { 6 $old = $this->value; 7 $this->value += $this->sum; 8 $this->sum = $old; 9 } 10 $this->key++; 11 } 12 public function valid() { 13 return ($this->value < PHP_INT_MAX); 14 } 15 }

Slide 30

Slide 30 text

Usage example 1 // print the Fibonacci numbers until PHP_INT_MAX 2 foreach ($test = new Fibonacci() as $key => $value) { 3 printf("%d) %d\n", $key, $value); 4 } 5 6 // print the first 10 Fibonacci's numbers 7 $num = new Fibonacci(); 8 for ($i = 0; $i < 10; $i++) { 9 printf("%d) %d\n", $i, $num->current()); 10 $num->next(); 11 }

Slide 31

Slide 31 text

Visitor Problem: separate an algorithm from an object structure on which it operates Solution: uses the Visitor pattern that allows one to add new virtual functions to a family of classes without modifying the classes themselves

Slide 32

Slide 32 text

Visitor: UML diagram

Slide 33

Slide 33 text

Implementation 1 interface Visited { 2 public function accept(Visitor $visitor); 3 } 4 5 class VisitedArray implements Visited 6 { 7 protected $elements = array(); 8 public function addElement($element){ 9 $this->elements[]=$element; 10 } 11 public function getSize(){ 12 return count($this->elements); 13 } 14 public function accept(Visitor $visitor){ 15 $visitor->visit($this); 16 } 17 }

Slide 34

Slide 34 text

Implementation (2) 1 interface Visitor { 2 public function visit(VisitedArray $elements); 3 } 4 5 class DataVisitor implements Visitor 6 { 7 protected $info; 8 public function visit(VisitedArray $visitedArray){ 9 $this->info = sprintf ("The array has %d elements", 10 $visitedArray->getSize()); 11 } 12 public function getInfo(){ 13 return $this->info; 14 } 15 }

Slide 35

Slide 35 text

Usage example 1 $visitedArray = new VisitedArray(); 2 3 $visitedArray->addElement('Element 1'); 4 $visitedArray->addElement('Element 2'); 5 $visitedArray->addElement('Element 3'); 6 7 $dataVisitor = new DataVisitor(); 8 $visitedArray->accept($dataVisitor); 9 $dataVisitor->visit($visitedArray); 10 11 printf( 12 "Info from visitor object: %s\n", 13 $dataVisitor->getInfo() 14 );

Slide 36

Slide 36 text

Decorator Problem: add functionalities to an existing object dynamically, without extend it Solution: use the Decorator pattern to alter or decorate portions of an existing object’s content or functionality without modifying the structure of the original object.

Slide 37

Slide 37 text

Decorator: UML diagram

Slide 38

Slide 38 text

Implementation 1 interface HtmlElement 2 { 3 public function __toString(); 4 public function getName(); 5 } 6 class InputText implements HtmlElement 7 { 8 protected $name; 9 public function __construct($name) { 10 $this->name = $name; 11 } 12 public function getName() { 13 return $this->name; 14 } 15 public function __toString() { 16 return "name}\"" 18 . "name=\"{$this->name}\" />\n"; 19 } 20 }

Slide 39

Slide 39 text

Implementation (2) 1 abstract class HtmlDecorator implements HtmlElement 2 { 3 protected $element; 4 public function __construct(HtmlElement $input) { 5 $this->element = $input; 6 } 7 public function getName() { 8 return $this->element->getName(); 9 } 10 public function __toString() { 11 return $this->element->__toString(); 12 } 13 }

Slide 40

Slide 40 text

Implementation (3) 1 class LabelDecorator extends HtmlDecorator { 2 protected $label; 3 public function setLabel($label) { 4 $this->label = $label; 5 } 6 public function __toString() { 7 $name = $this->getName(); 8 return "" 9 . $this->label . "\n" 10 . $this->element->__toString(); 11 } 12 }

Slide 41

Slide 41 text

Implementation (4) 1 class ErrorDecorator extends HtmlDecorator { 2 protected $error; 3 public function setError($message) { 4 $this->error = $message; 5 } 6 public function __toString() { 7 return $this->element->__toString() . 8 "{$this->error}\n"; 9 } 10 }

Slide 42

Slide 42 text

Usage example 1 // Add a label to the input text 2 $input = new InputText('nickname'); 3 $labelled = new LabelDecorator($input); 4 $labelled->setLabel('Nickname:'); 5 printf("%s\n", $labelled); 6 7 // Add an error message to the input text 8 $input = new InputText('nickname'); 9 $error = new ErrorDecorator($input); 10 $error->setError('You must enter a unique nickname'); 11 printf("%s\n", $error); 12 13 // Add a label and an error message to the input text 14 $input = new InputText('nickname'); 15 $labelled = new LabelDecorator($input); 16 $labelled->setLabel('Nickname:'); 17 $error = new ErrorDecorator($labelled); 18 $error->setError('You must enter a unique nickname'); 19 printf("%s\n", $error);

Slide 43

Slide 43 text

Section 1 Controllers and Workflow

Slide 44

Slide 44 text

Fundamental Patterns Strategy/Adapter/Command Factory Subject/Observer

Slide 45

Slide 45 text

Strategy Adapter Command

Slide 46

Slide 46 text

Strategy

Slide 47

Slide 47 text

The problem You've written code for which you need interchangeable algorithms. Based on the route, you'd handle a request differently. Based on console environment, you might need different line endings. You've got a large switch statement with many cases.

Slide 48

Slide 48 text

Strategy: UML diagram

Slide 49

Slide 49 text

Adapter

Slide 50

Slide 50 text

The problem You have domain-specific code that you want to use that doesn't follow interfaces of your toolkit/framework. You wrote an XML-RPC server, and now want to route to it. You have a data transfer object that could easily be re-purposed as a request You want to consume another object as a command, but it doesn't follow the interface you've defined.

Slide 51

Slide 51 text

Adapter: UML diagram

Slide 52

Slide 52 text

Implementation Step 1: Extract an interface 1 interface Handler 2 { 3 public function handle(Request $request); 4 }

Slide 53

Slide 53 text

Implementation Step 2: Compose a strategy 1 class Request 2 { 3 protected $handler; 4 public function setHandler(Handler $handler); 5 public function handle() 6 { 7 return $this->handler->handle($this); 8 } 9 }

Slide 54

Slide 54 text

Implementation Step 3: Class to be adapted 1 class Paste 2 { 3 public function create( 4 $content, $language = 'php' 5 ) { 6 // Return array of errors, 7 // or array representing paste 8 } 9 }

Slide 55

Slide 55 text

Implementation Extension: Implement the desired interface in an extending object. 1 class PasteAdapter extends Paste implements Handler 2 { 3 public function handle(Request $request) 4 { 5 $post = $request->getPost(); 6 $content = $post->get('content', ''); 7 $lang = $post->get('lang', '') 8 return $this->create($content, $lang); 9 } 10 }

Slide 56

Slide 56 text

Implementation Composition: Implement the desired interface, and inject the object being adapted. 1 class PasteAdapter implements Handler 2 { 3 protected $paste; 4 public function setPaste(Paste $paste); 5 public function handle(Request $request) 6 { 7 $post = $request->getPost(); 8 $content = $post->get('content', ''); 9 $lang = $post->get('lang', '') 10 return $this->paste 11 ->create($content, $lang); 12 } 13 }

Slide 57

Slide 57 text

Command

Slide 58

Slide 58 text

The Problem You have a lot of metadata that needs to be passed to one or more other objects. You discover you're coupling implementation details inside an object that should deal with abstractions. You're passing around query, post, header, and additional collections. You're passing around a set of common objects as individual arguments. The main context object shouldn't need to know what specific objects need to be passed to collaborators.

Slide 59

Slide 59 text

Command: UML diagram

Slide 60

Slide 60 text

Implementation Step 1: Extract a value object 1 interface Request 2 { 3 public function getUri(); 4 public function getQuery(); 5 public function getPost(); 6 public function getHeaders(); 7 }

Slide 61

Slide 61 text

Implementation Step 2: Create an interface for strategies/commands 1 interface Handler 2 { 3 public function dispatch(Request $request); 4 }

Slide 62

Slide 62 text

Implementation Step 3: Write handlers that delegate to other objects 1 class RoutePath { 2 public function route($path; 3 } 4 5 class PathHandler implements Handler { 6 protected $routePath; 7 public function dispatch(Request $request) { 8 $uri = $request->getUri(); 9 $path = $uri->getPath(); 10 $this->routePath->route($path); 11 } 12 }

Slide 63

Slide 63 text

Implementation Step 4: Compose the strategies/commands 1 class RequestHandler 2 { 3 protected $handlers = array(); 4 protected $request; 5 6 public function addHandler(Handler $handler); 7 public function setRequest(Request $request); 8 9 public function dispatch() 10 { 11 foreach ($this->handlers as $handler) { 12 $handler->dispatch($this->request); 13 } 14 } 15 }

Slide 64

Slide 64 text

Creational Patterns

Slide 65

Slide 65 text

Factory

Slide 66

Slide 66 text

The problem You know that you need an object of a specified type, but the concrete implementation will be determined dynamically. The controller will vary based on request. How pagination occurs will vary based on persistence. Validators will vary based on element.

Slide 67

Slide 67 text

Factory: UML diagram

Slide 68

Slide 68 text

Implementation Step 1: Extract the common interface 1 interface Handler 2 { 3 public function handle(Request $request); 4 }

Slide 69

Slide 69 text

Implementation Step 1a: Define a standard method for object creation 1 interface Handler 2 { 3 // Usually one of: 4 public function __construct($options); 5 // or: 6 public static function factory($options); 7 }

Slide 70

Slide 70 text

Implementation Step 2: Define a factory that returns objects of that interface 1 interface Factory 2 { 3 /** 4 * @return Handler 5 */ 6 public function create($type); 7 }

Slide 71

Slide 71 text

Implementation Step 3: Compose and consume a factory to get the concrete implementations 1 class RequestHandler 2 { 3 protected $factory; 4 public function setFactory(Factory $factory); 5 public function handle(Request $request) 6 { 7 $type = $this->request->getController(); 8 $handler = $this->factory->create($type); 9 return $handler->handle($request); 10 } 11 }

Slide 72

Slide 72 text

Related Patterns Inversion of Control Dependency Injection Container Service Locator

Slide 73

Slide 73 text

Service Locator 1 $services->setFactory('foo', function ($services) { 2 // do some work, and create and return 3 // an object instance 4 return $foo; 5 }); 6 $foo = $services->get('foo');

Slide 74

Slide 74 text

Dependency Injection Container 1 $object = $dic->get('Some\Classname');

Slide 75

Slide 75 text

Subject/Observer SignalSlot Event Handler

Slide 76

Slide 76 text

The Problem You have an indeterminate number of objects that need notifications of certain state changes. When a transaction happens, notify a CSR, and send a confirmation email. When a commit is made, each of the webhooks must be notified.

Slide 77

Slide 77 text

The Problem (2) You need to be able to introduce cross-cutting concerns at specific cut points in your application. You need to log the various workflows of the application. You want to be able to introduce caching at a later date if it's needed.

Slide 78

Slide 78 text

The Problem (3) You may need to halt execution or modify the workflow if certain conditions are met, but the conditions are not semantically part of the subject. A controller issues a redirect. A controller determines it cannot handle a request. Or another controller can! Based on the Accept header, we need to use a different renderer.

Slide 79

Slide 79 text

Subject Observer: UML diagram

Slide 80

Slide 80 text

SignalSlot: UML diagram

Slide 81

Slide 81 text

Event Handler: UML diagram

Slide 82

Slide 82 text

Implementation Subject/Observer 1 class Subject 2 { 3 protected $observers = array(); 4 public function addObserver(Observer $observer) 5 { 6 $this->observers[] = $observer 7 } 8 public function execute() 9 { 10 foreach ($this->observers as $observer) { 11 $observer->notify($this); 12 } 13 } 14 }

Slide 83

Slide 83 text

Implementation Subject/Observer (cont) 1 interface Observer 2 { 3 public function notify(Subject $subject); 4 }

Slide 84

Slide 84 text

Implementation SignalSlot 1 interface Signals 2 { 3 public function connect($signal, $callable); 4 public function emit($signal, $argv = null); 5 }

Slide 85

Slide 85 text

Implementation SignalSlot (cont) 1 class Foo 2 { 3 protected $signals; 4 public function setSignals(Signals $signals); 5 6 public function bar($baz, $bat) 7 { 8 $this->signals->emit('bar', $baz, $bat); 9 } 10 }

Slide 86

Slide 86 text

Implementation SignalSlot (cont) 1 $signals = new SignalSlotManager(); 2 $signals->connect('bar', function ($baz, $bat) { 3 printf('%s:%s', $baz, $bat); 4 }); 5 6 $foo = new Foo(); 7 $foo->setSignals($signals); 8 $foo->bar('do', 'something');

Slide 87

Slide 87 text

Implementation Event Handler 1 interface Event { 2 public function getName(); 3 public function getTarget(); 4 public function setParams(array $params); 5 public function getParams(); 6 } 7 8 interface Events { 9 public function attach($name, $callback); 10 public function trigger( 11 $name, $target, Event $event 12 ); 13 }

Slide 88

Slide 88 text

Implementation Event Handler (cont) 1 class Foo 2 { 3 protected $events; 4 public function setEventHandler(Events $events); 5 public function bar($baz, $bat) 6 { 7 $event = new Event(); 8 $event->setParams(array( 9 'baz' => $baz, 10 'bat' => $bat, 11 )); 12 $this->events->trigger( 13 'bar', $this, $event); 14 } 15 }

Slide 89

Slide 89 text

Implementation Event Handler (cont) 1 $events = new EventHandler(); 2 $events->attach('bar', function (Event $e) { 3 $params = $e->getParams(); 4 $class = get_class($e->getTarget()); 5 printf('[%s][%s] %s', 6 $e->getName(), 7 $class, 8 json_encode($params) 9 ); 10 }); 11 $foo = new Foo; 12 $foo->setEvents($events); 13 $foo->bar('do', 'something');

Slide 90

Slide 90 text

More concepts Short circuiting If a listener returns a particular response, end early Allow a listener to halt the event loop Response aggregation and introspection Global/Static manager, or per object? Event/signal naming

Slide 91

Slide 91 text

Summary We examined patterns around interchangeability of algorithms (Strategy, Adapter, Command). We examined how to dynamically build objects of a specific type based on provided criteria (Factory, Builder, IoC). We examined how to compose objects that we can notify of changes or important stages of the application workflow (Subject/Observer, SignalSlot, Event Handler).

Slide 92

Slide 92 text

Assignment

Slide 93

Slide 93 text

Build a dispatcher Controller will be given via a query string argument. Comma-delimit multiple controllers. Only instantiate the controllers specified. Do not use the fully-qualified class names in the query string. Add a non-Controller handler that executes for every request and which logs the query string argument. Use as many of the discussed patterns as possible.

Slide 94

Slide 94 text

Modeling patterns

Slide 95

Slide 95 text

Patterns we'll cover Prototype Mapper Repository Entity, Value Object, Values (Some others in brief)

Slide 96

Slide 96 text

Why These Particular Patterns? Again, a common diction and vocabulary These are taken in part from Domain Driven Design (Eric Evans) Tools to find a suitable level of abstraction

Slide 97

Slide 97 text

Prototype Category: Creational Pattern, typically used by code promoting extension Problem: objects that need to generate objects as part of a normal workflow

Slide 98

Slide 98 text

Prototype First Solution: let your primary object create (call new) for every new object it must create Cons: When object creation is complex or becomes custom, this workflow then requires overriding and customization of the parent class Better Solution: Use the prototype pattern

Slide 99

Slide 99 text

Prototype UML Diagram

Slide 100

Slide 100 text

Implementation 1 interface PrototypicalInterface { 2 /* Nothing new required */ 3 public function initialize($values); 4 } 5 6 class PrototypicalFoo 7 implements PrototypicalInterface { 8 public function __construct() {} 9 } 10 11 class PrototypicalBar 12 implements PrototypicalInterface { 13 public function __construct(\mysqli $mysqli) {} 14 }

Slide 101

Slide 101 text

Implementation (2) 1 class PrototypeConsumer { 2 protected $p; 3 public function __construct( 4 PrototypicalInterface $p 5 ) { 6 $this->p = $p; 7 } 8 public function operation() { 9 $p = clone $this->p; 10 $p->initialize($this->values); 11 return $p; // new instance, 12 // based off the foo 13 } 14 }

Slide 102

Slide 102 text

Usage 1 $pf = new PrototypicalFoo; 2 $pc = new PrototypeConsumer(); 3 $p = $pc->operation(); // a clone $pf, specialized 4 // by the $pc during 5 // operation()

Slide 103

Slide 103 text

Story: How did I decide to use this once? Zend Framework's ResultSet object for Zend\Db Goals: Provide a ResultSet interface Allow consumers to build their own specialized ResultSet object Will create a ResultSet per call to query(), execute() on a Stmt.

Slide 104

Slide 104 text

Found Inside ZF 1.x 1 // inside Zend_Db_Table::find 2 return new $rowsetClass(array( 3 'table' => $this, 4 'rowClass' => $this->getRowClass(), 5 'stored' => true 6 ));

Slide 105

Slide 105 text

Found Inside ZF 2.x 1 // inside Zend\Db\TableGateway\AbstractTableGatway::select 2 $result = $statement->execute(); 3 4 // build result set 5 $resultSet = clone $this->resultSetPrototype; 6 $resultSet->initialize($result);

Slide 106

Slide 106 text

Usage All I care is that you implement ResultSetInterface 1 $table = new TableGatgeway( 2 'my_table', 3 $adapter, 4 null, // features 5 new HydratingResultSet(new MyTableHydrator) 6 );

Slide 107

Slide 107 text

Mapper / Data Mapper Category: Base Pattern / Data Access Pattern Problem: Need to change an object/array/data into an object (and visa-versa) between two systems that you want to keep their API's separate and code ignorant of each other.

Slide 108

Slide 108 text

Mapper First Solution: There are many: cast to stdClass, allow entity object to sort out a translation, use data source specific solution (PDO::FETCH_CLASS for example). Cons: Mixed levels of separation of concerns, repeated code (in the case of translations), etc. Better Solution: Use a Mapper object.

Slide 109

Slide 109 text

Mapper UML Diagram

Slide 110

Slide 110 text

Implementation 1 class Artist { 2 public $name, $bio; 3 /** @var Album[] */ 4 public $albums = array(); 5 6 public function getName() { 7 return $this->name; 8 } 9 // ... 10 }

Slide 111

Slide 111 text

Implementation 1 class ArtistMapper { 2 public function mapArrayToArtist( 3 array $data, Artist $artist = null 4 ) { 5 $artist = ($artist) ?: new Artist; 6 $artist->firstName = $data['first_name']; 7 $artist->lastName = $data['last_name']; 8 9 $album = new Album; 10 $album->title = $data['album_1_title']; 11 $artist->albums[] = $album; 12 return $artist; 13 } 14 }

Slide 112

Slide 112 text

Implementation (2) 1 public function mapArtistToArray( 2 Artist $artist 3 ) { 4 return array( 5 'first_name' => $artist->firstName, 6 'last_name' => $artist->lastName 7 ); 8 }

Slide 113

Slide 113 text

Usage 1 $artistData = $dbMapper; 2 $artistMapper = new ArtistDataMapper; 3 $artist = $artistMapper->mapArrayToArtist( 4 $personArray 5 ); // returns Artist object

Slide 114

Slide 114 text

Repository Category: Data Access Problem: You don't want SQL in your controller code. You want to hide the persistence implementation from the Model's API (Persistence Ignorance).

Slide 115

Slide 115 text

Repository First Solution: create collection returning methods on your Data Access object. Cons: Public API of the Data Access object becomes confused and overloaded with semi-related methods Better Solution: Use the repository pattern

Slide 116

Slide 116 text

Sidetrack: Persistence Ignorance & Interfaces "Persistence Ignorance" is the idea that at a particular level of your abstraction, the API knows nothing about (the details) how something is persisted Implementations of a Repository can deal with persistence, but this should not be exposed in the API of this class (or the interface for the Repository)

Slide 117

Slide 117 text

Repository UML Diagram

Slide 118

Slide 118 text

Implementation 1 interface TrackRepositoryInterface { 2 // @return Track[] 3 public function findAll(); 4 public function findById($id); 5 6 public function store(Track $track); 7 public function remove(Track $track); 8 }

Slide 119

Slide 119 text

Implementation (2) 1 class DbTrackRepository 2 implements TrackRepositoryInterface { 3 public function __construct( 4 TrackDbMapper $mapper 5 ) {} 6 /** ... **/ 7 }

Slide 120

Slide 120 text

Usage 1 $trackRepo = new DbTrackRepository( 2 $services->get('TrackMapper') 3 ); 4 $tracks = $trackRepo->findAll(); 5 foreach ($tracks as $track) { 6 // do something interesting 7 }

Slide 121

Slide 121 text

Entity, Value Object, and Values Category: Object Modeling Problem: In both cases, you want to encapsulate a set of data into a conceptual and physical object. This helps separate out the data from the functional components themselves. Sometimes, each object has an identity, a way of identifying a set of data, sometimes not.

Slide 122

Slide 122 text

Entity, Value Object, and Values First Solution: Use an array, Return an array from a function or method call, or return a stdClass from a function or method call. Cons: Using an array or stdClass does not guarantee the set of data inside these structures, makes validation harder, makes typing impossible, and does not enforce allow for data safety. Better Solution: Use an Entity or a Value Object

Slide 123

Slide 123 text

Whats the Difference An Entity has an identity and a value object does not. Both are generally POPO's (Plain old PHP objects).

Slide 124

Slide 124 text

Value Objects By definition, identity free and immutable. Values are simply put, any scalar in PHP (for all intents and purposes). Two separate Entities can share the same reference to a Value Object.

Slide 125

Slide 125 text

Entity 1 class Artist { 2 public $id; // has identity! 3 public $name; // has identity! 4 public $yearFormed; 5 }

Slide 126

Slide 126 text

The Classic Example In PHP ... Is not what you think it is. Traditionally, a DateTime object is generally considered a Value Object. Here is why it is not in PHP...

Slide 127

Slide 127 text

Code sample PHP's DateTime 1 class DateTime { 2 public function modify(/* string */ $modify); 3 public function set*(); 4 public function add(DateInterval $interval); 5 }

Slide 128

Slide 128 text

Value Object 1 /** 2 * NOT PHP's DATETIME!!!! 3 */ 4 class Date { 5 public $year; 6 public $month; 7 public $day; 8 }

Slide 129

Slide 129 text

Value 1 $artist = new Artist; 2 3 // name is a value, a string 4 $artist->name = 'Splender';

Slide 130

Slide 130 text

Other Patterns There are a number of patterns/diction not discussed, that need to be at least mentioned What is an Layered Architecture? What are Services? What is an Aggregate + Aggregate Root?

Slide 131

Slide 131 text

Layered Architecture

Slide 132

Slide 132 text

Layered Architecture A way of dividing out software conceptually In PHP, this might happen with some usage of namespaces The type of pattern it implements implies the layer of code it belongs to

Slide 133

Slide 133 text

Services An overly used term, has many different contexts Service Layer: separate abstraction layer between controllers and models Model Services: (DDD) A place where "workflows/functions that have no natural place in a value object/entity" Dependency Injection / Application Architecture: shared objects, dependencies (Service Locator)

Slide 134

Slide 134 text

Aggregate & Aggregate Root (DDD) A Domain Driven Design Term Aggregate: the series of objects in a model bound together by references and associations Aggregate Root: Only object outside members can hold a reference to, the "entry object", the primary object

Slide 135

Slide 135 text

Exercise Let's build something with all that we've learned!

Slide 136

Slide 136 text

Base Application https://github.com/ralphschindler/PatternsTutorialApp/

Slide 137

Slide 137 text

The Idea I there is money in sharing playlists online. I am not sure what the business will be, but I know it centers around a playlist We need to be able to model Track, Arist and Album information We might want to be able to pull information from web services

Slide 138

Slide 138 text

The Domain Model

Slide 139

Slide 139 text

Switch To IDE Time to switch to IDE, lets explore code

Slide 140

Slide 140 text

References E.Gamma, R.Helm, R.Johnson, J.Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Addison- Wesley Professional, 1994 Aaron Saray, Professional PHP Design Patterns, Wrox 2004 Jason E. Sweat, Guide to PHP Design Patterns, Marco Tabini & Associates 2004 Matt Zandstra , PHP Objects, Patterns and Practice, Apress (3 edition) 2010

Slide 141

Slide 141 text

References (2) Matthew Weier O'Phinney, Proxies in PHP Giorgio Sironi, Pratical PHP Patterns: Decorator PHP Manual, The Iterator Interface

Slide 142

Slide 142 text

Resources https://github.com/ezimuel/PHP-design-patterns https://github.com/ralphschindler/PatternsTutorialApp

Slide 143

Slide 143 text

Feedback http://joind.in/6857

Slide 144

Slide 144 text

Thank You