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.

D3a1203bb9b132944427746ec3eae323?s=128

Enrico Zimuel

October 22, 2012
Tweet

Transcript

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

    Zimuel ZendCon 2012
  2. What are Design Patterns?

  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)”
  4. Elements of design patterns Describe the problem 1. Describe the

    solution 2. Describe when it is applicable 3.
  5. A Grammar for Software Development

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

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

  8. Our presentation pattern Outline the problem Name the pattern Describe

    how the pattern implementation resolves the problem
  9. Foundation patterns

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

  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
  12. Bridge: UML diagram

  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 }
  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 }
  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)
  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.
  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 }
  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 }
  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
  20. Proxy: UML diagram

  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 }
  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 }
  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
  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 }
  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
  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)
  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 }
  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 ...
  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 }
  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 }
  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
  32. Visitor: UML diagram

  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 }
  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 }
  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 );
  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.
  37. Decorator: UML diagram

  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 "<input type=\"text\" " 17 . "id=\"{$this->name}\"" 18 . "name=\"{$this->name}\" />\n"; 19 } 20 }
  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 }
  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 "<label for=\"{$name}\">" 9 . $this->label . "</label>\n" 10 . $this->element->__toString(); 11 } 12 }
  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 "<span>{$this->error}</span>\n"; 9 } 10 }
  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);
  43. Section 1 Controllers and Workflow

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

  45. Strategy Adapter Command

  46. Strategy

  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.
  48. Strategy: UML diagram

  49. Adapter

  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.
  51. Adapter: UML diagram

  52. Implementation Step 1: Extract an interface 1 interface Handler 2

    { 3 public function handle(Request $request); 4 }
  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 }
  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 }
  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 }
  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 }
  57. Command

  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.
  59. Command: UML diagram

  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 }
  61. Implementation Step 2: Create an interface for strategies/commands 1 interface

    Handler 2 { 3 public function dispatch(Request $request); 4 }
  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 }
  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 }
  64. Creational Patterns

  65. Factory

  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.
  67. Factory: UML diagram

  68. Implementation Step 1: Extract the common interface 1 interface Handler

    2 { 3 public function handle(Request $request); 4 }
  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 }
  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 }
  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 }
  72. Related Patterns Inversion of Control Dependency Injection Container Service Locator

  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');
  74. Dependency Injection Container 1 $object = $dic->get('Some\Classname');

  75. Subject/Observer SignalSlot Event Handler

  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.
  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.
  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.
  79. Subject Observer: UML diagram

  80. SignalSlot: UML diagram

  81. Event Handler: UML diagram

  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 }
  83. Implementation Subject/Observer (cont) 1 interface Observer 2 { 3 public

    function notify(Subject $subject); 4 }
  84. Implementation SignalSlot 1 interface Signals 2 { 3 public function

    connect($signal, $callable); 4 public function emit($signal, $argv = null); 5 }
  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 }
  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');
  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 }
  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 }
  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');
  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
  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).
  92. Assignment

  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.
  94. Modeling patterns

  95. Patterns we'll cover Prototype Mapper Repository Entity, Value Object, Values

    (Some others in brief)
  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
  97. Prototype Category: Creational Pattern, typically used by code promoting extension

    Problem: objects that need to generate objects as part of a normal workflow
  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
  99. Prototype UML Diagram

  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 }
  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 }
  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()
  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.
  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 ));
  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);
  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 );
  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.
  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.
  109. Mapper UML Diagram

  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 }
  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 }
  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 }
  113. Usage 1 $artistData = $dbMapper; 2 $artistMapper = new ArtistDataMapper;

    3 $artist = $artistMapper->mapArrayToArtist( 4 $personArray 5 ); // returns Artist object
  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).
  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
  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)
  117. Repository UML Diagram

  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 }
  119. Implementation (2) 1 class DbTrackRepository 2 implements TrackRepositoryInterface { 3

    public function __construct( 4 TrackDbMapper $mapper 5 ) {} 6 /** ... **/ 7 }
  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 }
  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.
  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
  123. Whats the Difference An Entity has an identity and a

    value object does not. Both are generally POPO's (Plain old PHP objects).
  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.
  125. Entity 1 class Artist { 2 public $id; // has

    identity! 3 public $name; // has identity! 4 public $yearFormed; 5 }
  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...
  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 }
  128. Value Object 1 /** 2 * NOT PHP's DATETIME!!!! 3

    */ 4 class Date { 5 public $year; 6 public $month; 7 public $day; 8 }
  129. Value 1 $artist = new Artist; 2 3 // name

    is a value, a string 4 $artist->name = 'Splender';
  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?
  131. Layered Architecture

  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
  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)
  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
  135. Exercise Let's build something with all that we've learned!

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

  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
  138. The Domain Model

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

    code
  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
  141. References (2) Matthew Weier O'Phinney, Proxies in PHP Giorgio Sironi,

    Pratical PHP Patterns: Decorator PHP Manual, The Iterator Interface
  142. Resources https://github.com/ezimuel/PHP-design-patterns https://github.com/ralphschindler/PatternsTutorialApp

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

  144. Thank You