$30 off During Our Annual Pro Sale. View Details »

Design Patterns Bootcamp

Design Patterns Bootcamp

Design patterns are not a fashion statement; they're a recipe for solving common software architectural problems. How can you get access to object dependencies? How can you build a system that allows for arbitrary code to be notified of changes? How can you provide information between two objects in a consistent and fault-tolerant fashion? Join us as we provide an overview of common design patterns, demonstrating the various use cases each attempts to solve, as well as discussing limitations and gotchs. We will attempt to show the simplest possible PHP implementations, as well as real-world examples from OSS projects.

Enrico Zimuel

October 22, 2012
Tweet

More Decks by Enrico Zimuel

Other Decks in Programming

Transcript

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

    View Slide

  2. What are Design Patterns?

    View Slide

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

    View Slide

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

    View Slide

  5. A Grammar for Software Development

    View Slide

  6. What design patterns are not
    Copy and paste solutions
    Standard implementation

    View Slide

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

    View Slide

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

    View Slide

  9. Foundation patterns

    View Slide

  10. Patterns we'll cover
    Bridge
    Facade
    Proxy
    Iterator
    Visitor
    Decorator

    View Slide

  11. 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

    View Slide

  12. Bridge: UML diagram

    View Slide

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

    View Slide

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

    View Slide

  15. 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)

    View Slide

  16. 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.

    View Slide

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

    View Slide

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

    View Slide

  19. 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

    View Slide

  20. Proxy: UML diagram

    View Slide

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

    View Slide

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

    View Slide

  23. 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

    View Slide

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

    View Slide

  25. 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

    View Slide

  26. 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)

    View Slide

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

    View Slide

  28. 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 ...

    View Slide

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

    View Slide

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

    View Slide

  31. 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

    View Slide

  32. Visitor: UML diagram

    View Slide

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

    View Slide

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

    View Slide

  35. 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 );

    View Slide

  36. 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.

    View Slide

  37. Decorator: UML diagram

    View Slide

  38. 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 "17 . "id=\"{$this->name}\""
    18 . "name=\"{$this->name}\" />\n";
    19 }
    20 }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  42. 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);

    View Slide

  43. Section 1
    Controllers and Workflow

    View Slide

  44. Fundamental Patterns
    Strategy/Adapter/Command
    Factory
    Subject/Observer

    View Slide

  45. Strategy
    Adapter
    Command

    View Slide

  46. Strategy

    View Slide

  47. 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.

    View Slide

  48. Strategy: UML diagram

    View Slide

  49. Adapter

    View Slide

  50. 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.

    View Slide

  51. Adapter: UML diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. Command

    View Slide

  58. 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.

    View Slide

  59. Command: UML diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  64. Creational Patterns

    View Slide

  65. Factory

    View Slide

  66. 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.

    View Slide

  67. Factory: UML diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  72. Related Patterns
    Inversion of Control
    Dependency Injection Container
    Service Locator

    View Slide

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

    View Slide

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

    View Slide

  75. Subject/Observer
    SignalSlot
    Event Handler

    View Slide

  76. 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.

    View Slide

  77. 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.

    View Slide

  78. 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.

    View Slide

  79. Subject Observer: UML diagram

    View Slide

  80. SignalSlot: UML diagram

    View Slide

  81. Event Handler: UML diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  90. 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

    View Slide

  91. 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).

    View Slide

  92. Assignment

    View Slide

  93. 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.

    View Slide

  94. Modeling patterns

    View Slide

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

    View Slide

  96. 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

    View Slide

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

    View Slide

  98. 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

    View Slide

  99. Prototype UML Diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  103. 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.

    View Slide

  104. 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 ));

    View Slide

  105. 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);

    View Slide

  106. 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 );

    View Slide

  107. 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.

    View Slide

  108. 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.

    View Slide

  109. Mapper UML Diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  114. 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).

    View Slide

  115. 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

    View Slide

  116. 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)

    View Slide

  117. Repository UML Diagram

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  121. 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.

    View Slide

  122. 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

    View Slide

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

    View Slide

  124. 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.

    View Slide

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

    View Slide

  126. 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...

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  130. 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?

    View Slide

  131. Layered Architecture

    View Slide

  132. 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

    View Slide

  133. 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)

    View Slide

  134. 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

    View Slide

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

    View Slide

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

    View Slide

  137. 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

    View Slide

  138. The Domain Model

    View Slide

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

    View Slide

  140. 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

    View Slide

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

    View Slide

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

    View Slide

  143. Feedback
    http://joind.in/6857

    View Slide

  144. Thank You

    View Slide