Design Bootcamp

download Design Bootcamp

of 144

Transcript of Design Bootcamp

  • 7/28/2019 Design Bootcamp

    1/144

    Design Patterns

    BootcampRalph Schindler, Matthew Weier O'Phinney, and Enrico Zimuel

    ZendCon 2012

  • 7/28/2019 Design Bootcamp

    2/144

    What are Design Patterns?

  • 7/28/2019 Design Bootcamp

    3/144

    A formal way of documenting a solution to a design problem in

    a particular field of expertise. (http://en.wikipedia.org/wiki/Design_patterns)

  • 7/28/2019 Design Bootcamp

    4/144

    Elements of design patternsDescribe the problem1.

    Describe the solution2.

    Describe when it is applicable3.

  • 7/28/2019 Design Bootcamp

    5/144

    A Grammar for Software Development

  • 7/28/2019 Design Bootcamp

    6/144

    What design patterns are notCopy and paste solutions

    Standard implementation

  • 7/28/2019 Design Bootcamp

    7/144

    What we'll cover todayFoundation patterns

    Behavioral patterns

    Modeling patterns

  • 7/28/2019 Design Bootcamp

    8/144

    Our presentation patternOutline the problem

    Name the pattern

    Describe how the pattern implementation resolves the problem

  • 7/28/2019 Design Bootcamp

    9/144

    Foundation patterns

  • 7/28/2019 Design Bootcamp

    10/144

    Patterns we'll coverBridge

    Facade

    Proxy

    Iterator

    Visitor

    Decorator

  • 7/28/2019 Design Bootcamp

    11/144

    BridgeProblem: 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

  • 7/28/2019 Design Bootcamp

    12/144

    Bridge: UML diagram

  • 7/28/2019 Design Bootcamp

    13/144

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

  • 7/28/2019 Design Bootcamp

    14/144

    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 }

  • 7/28/2019 Design Bootcamp

    15/144

    Usage example1 $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)

  • 7/28/2019 Design Bootcamp

    16/144

    FacadeProblem: 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.

  • 7/28/2019 Design Bootcamp

    17/144

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

  • 7/28/2019 Design Bootcamp

    18/144

    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 }

  • 7/28/2019 Design Bootcamp

    19/144

    ProxyProblem 1: manage "expensive to create" objects, lazy-loading

    them only on first access

    Problem 2: provide a local object representation of remote systemprocesses

    Problem 3: consuming and controlling access to another object

    Solution: Design a proxy class that access/extend the object,

    overriding one or more methods

  • 7/28/2019 Design Bootcamp

    20/144

    Proxy: UML diagram

  • 7/28/2019 Design Bootcamp

    21/144

    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 }

  • 7/28/2019 Design Bootcamp

    22/144

    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 }

  • 7/28/2019 Design Bootcamp

    23/144

    Usage example1 $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 unnecessary9

    10 // Expected output:

    11 // Loading test.png

    12 // Display test.png

    13 // Loading test.png

    14 // Display test.png

    15 // Display test.png

  • 7/28/2019 Design Bootcamp

    24/144

    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 }

  • 7/28/2019 Design Bootcamp

    25/144

    Usage example1 $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

  • 7/28/2019 Design Bootcamp

    26/144

    IteratorProblem: manipulate/traverse a collection of objects with a

    standard interface

    Solution: use the Iterator pattern that enables to traverse acontainer of objects

    PHP: PHP supports a standard Iterator interface (and Iterators

    classes in the SPL ready to be used)

  • 7/28/2019 Design Bootcamp

    27/144

    PHP Iterator interface1 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 }

  • 7/28/2019 Design Bootcamp

    28/144

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

  • 7/28/2019 Design Bootcamp

    29/144

    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 }

  • 7/28/2019 Design Bootcamp

    30/144

    Usage example1 // 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 }

  • 7/28/2019 Design Bootcamp

    31/144

    VisitorProblem: separate an algorithm from an object structure on which

    it operates

    Solution: uses the Visitor pattern that allows one to add new virtualfunctions to a family of classes without modifying the classes

    themselves

  • 7/28/2019 Design Bootcamp

    32/144

    Visitor: UML diagram

  • 7/28/2019 Design Bootcamp

    33/144

  • 7/28/2019 Design Bootcamp

    34/144

    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 }

  • 7/28/2019 Design Bootcamp

    35/144

    Usage example1 $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 );

  • 7/28/2019 Design Bootcamp

    36/144

    DecoratorProblem: add functionalities to an existing object dynamically,

    without extend it

    Solution: use the Decorator pattern to alter or decorate portions ofan existing objects content or functionality without modifying the

    structure of the original object.

  • 7/28/2019 Design Bootcamp

    37/144

    Decorator: UML diagram

  • 7/28/2019 Design Bootcamp

    38/144

  • 7/28/2019 Design Bootcamp

    39/144

  • 7/28/2019 Design Bootcamp

    40/144

    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 }

  • 7/28/2019 Design Bootcamp

    41/144

  • 7/28/2019 Design Bootcamp

    42/144

    Usage example1 // 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);

  • 7/28/2019 Design Bootcamp

    43/144

  • 7/28/2019 Design Bootcamp

    44/144

    Fundamental PatternsStrategy/Adapter/Command

    Factory

    Subject/Observer

  • 7/28/2019 Design Bootcamp

    45/144

    Strategy

    AdapterCommand

  • 7/28/2019 Design Bootcamp

    46/144

    Strategy

  • 7/28/2019 Design Bootcamp

    47/144

    The problemYou'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 lineendings.

    You've got a large switch statement with many cases.

  • 7/28/2019 Design Bootcamp

    48/144

    Strategy: UML diagram

  • 7/28/2019 Design Bootcamp

    49/144

    Adapter

  • 7/28/2019 Design Bootcamp

    50/144

  • 7/28/2019 Design Bootcamp

    51/144

    Adapter: UML diagram

  • 7/28/2019 Design Bootcamp

    52/144

    ImplementationStep 1: Extract an interface

    1 interface Handler

    2 {

    3 public function handle(Request $request);

    4 }

  • 7/28/2019 Design Bootcamp

    53/144

  • 7/28/2019 Design Bootcamp

    54/144

  • 7/28/2019 Design Bootcamp

    55/144

  • 7/28/2019 Design Bootcamp

    56/144

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

  • 7/28/2019 Design Bootcamp

    57/144

    Command

  • 7/28/2019 Design Bootcamp

    58/144

    The ProblemYou 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 specificobjects need to be passed to collaborators.

  • 7/28/2019 Design Bootcamp

    59/144

  • 7/28/2019 Design Bootcamp

    60/144

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

  • 7/28/2019 Design Bootcamp

    61/144

    ImplementationStep 2: Create an interface for strategies/commands

    1 interface Handler

    2 {

    3 public function dispatch(Request $request);

    4 }

  • 7/28/2019 Design Bootcamp

    62/144

  • 7/28/2019 Design Bootcamp

    63/144

    ImplementationStep 4: Compose the strategies/commands

    1 class RequestHandler

    2 {

    3 protected $handlers = array();

    4 protected $request;

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

  • 7/28/2019 Design Bootcamp

    64/144

  • 7/28/2019 Design Bootcamp

    65/144

    Factory

  • 7/28/2019 Design Bootcamp

    66/144

    The problemYou 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.

  • 7/28/2019 Design Bootcamp

    67/144

  • 7/28/2019 Design Bootcamp

    68/144

    ImplementationStep 1: Extract the common interface

    1 interface Handler

    2 {

    3 public function handle(Request $request);

    4 }

  • 7/28/2019 Design Bootcamp

    69/144

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

  • 7/28/2019 Design Bootcamp

    70/144

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

  • 7/28/2019 Design Bootcamp

    71/144

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

  • 7/28/2019 Design Bootcamp

    72/144

    Related PatternsInversion of Control

    Dependency Injection Container

    Service Locator

  • 7/28/2019 Design Bootcamp

    73/144

    Service Locator1 $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');

  • 7/28/2019 Design Bootcamp

    74/144

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

  • 7/28/2019 Design Bootcamp

    75/144

  • 7/28/2019 Design Bootcamp

    76/144

  • 7/28/2019 Design Bootcamp

    77/144

  • 7/28/2019 Design Bootcamp

    78/144

    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.

  • 7/28/2019 Design Bootcamp

    79/144

  • 7/28/2019 Design Bootcamp

    80/144

  • 7/28/2019 Design Bootcamp

    81/144

  • 7/28/2019 Design Bootcamp

    82/144

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

  • 7/28/2019 Design Bootcamp

    83/144

    ImplementationSubject/Observer (cont)

    1 interface Observer

    2 {

    3 public function notify(Subject $subject);

    4 }

  • 7/28/2019 Design Bootcamp

    84/144

    ImplementationSignalSlot

    1 interface Signals

    2 {

    3 public function connect($signal, $callable);

    4 public function emit($signal, $argv = null);

    5 }

  • 7/28/2019 Design Bootcamp

    85/144

    ImplementationSignalSlot (cont)

    1 class Foo

    2 {

    3 protected $signals;

    4 public function setSignals(Signals $signals);

    56 public function bar($baz, $bat)

    7 {

    8 $this->signals->emit('bar', $baz, $bat);

    9 }

    10 }

  • 7/28/2019 Design Bootcamp

    86/144

    ImplementationSignalSlot (cont)

    1 $signals = new SignalSlotManager();

    2 $signals->connect('bar', function ($baz, $bat) {

    3 printf('%s:%s', $baz, $bat);

    4 });

    56 $foo = new Foo();

    7 $foo->setSignals($signals);

    8 $foo->bar('do', 'something');

  • 7/28/2019 Design Bootcamp

    87/144

  • 7/28/2019 Design Bootcamp

    88/144

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

  • 7/28/2019 Design Bootcamp

    89/144

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

  • 7/28/2019 Design Bootcamp

    90/144

    More conceptsShort circuiting

    If a listener returns a particular response, end early

    Allow a listener to halt the event loop

    Response aggregation and introspectionGlobal/Static manager, or per object?

    Event/signal naming

  • 7/28/2019 Design Bootcamp

    91/144

  • 7/28/2019 Design Bootcamp

    92/144

    Assignment

  • 7/28/2019 Design Bootcamp

    93/144

    Build a dispatcherController will be given via a query string argument.

    Comma-delimitmultiple controllers.

    Only instantiate the controllers specified.

    Do not use the fully-qualified class names in the querystring.

    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.

  • 7/28/2019 Design Bootcamp

    94/144

    Modeling patterns

  • 7/28/2019 Design Bootcamp

    95/144

  • 7/28/2019 Design Bootcamp

    96/144

    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

  • 7/28/2019 Design Bootcamp

    97/144

    Prototype

    Category: Creational Pattern, typically used by code promoting

    extension

    Problem: objects that need to generate objects as part of a normal

    workflow

  • 7/28/2019 Design Bootcamp

    98/144

    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 parentclass

    Better Solution: Use the prototype pattern

  • 7/28/2019 Design Bootcamp

    99/144

    Prototype UML Diagram

  • 7/28/2019 Design Bootcamp

    100/144

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

  • 7/28/2019 Design Bootcamp

    101/144

    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 }

  • 7/28/2019 Design Bootcamp

    102/144

    Usage1 $pf = new PrototypicalFoo;

    2 $pc = new PrototypeConsumer();

    3 $p = $pc->operation(); // a clone $pf, specialized

    4 // by the $pc during

    5 // operation()

  • 7/28/2019 Design Bootcamp

    103/144

    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.

  • 7/28/2019 Design Bootcamp

    104/144

    Found Inside ZF 1.x1 // inside Zend_Db_Table::find

    2 return new $rowsetClass(array(

    3 'table' => $this,

    4 'rowClass' => $this->getRowClass(),

    5 'stored' => true

    6 ));

  • 7/28/2019 Design Bootcamp

    105/144

    Found Inside ZF 2.x1 // inside Zend\Db\TableGateway\AbstractTableGatway::select

    2 $result = $statement->execute();

    3

    4 // build result set

    5 $resultSet = clone $this->resultSetPrototype;

    6 $resultSet->initialize($result);

  • 7/28/2019 Design Bootcamp

    106/144

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

  • 7/28/2019 Design Bootcamp

    107/144

    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.

  • 7/28/2019 Design Bootcamp

    108/144

    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 (inthe case of translations), etc.

    Better Solution: Use a Mapper object.

  • 7/28/2019 Design Bootcamp

    109/144

    Mapper UML Diagram

  • 7/28/2019 Design Bootcamp

    110/144

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

  • 7/28/2019 Design Bootcamp

    111/144

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

  • 7/28/2019 Design Bootcamp

    112/144

    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 }

  • 7/28/2019 Design Bootcamp

    113/144

    Usage1 $artistData = $dbMapper;

    2 $artistMapper = new ArtistDataMapper;

    3 $artist = $artistMapper->mapArrayToArtist(

    4 $personArray

    5 ); // returns Artist object

  • 7/28/2019 Design Bootcamp

    114/144

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

  • 7/28/2019 Design Bootcamp

    115/144

    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

  • 7/28/2019 Design Bootcamp

    116/144

    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)

  • 7/28/2019 Design Bootcamp

    117/144

    Repository UML Diagram

  • 7/28/2019 Design Bootcamp

    118/144

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

  • 7/28/2019 Design Bootcamp

    119/144

    Implementation (2)1 class DbTrackRepository

    2 implements TrackRepositoryInterface {

    3 public function__construct(

    4 TrackDbMapper $mapper

    5 ) {}

    6 /** ... **/

    7 }

  • 7/28/2019 Design Bootcamp

    120/144

    Usage1 $trackRepo = new DbTrackRepository(

    2 $services->get('TrackMapper')

    3 );

    4 $tracks = $trackRepo->findAll();

    5 foreach ($tracks as $track) {

    6 // do something interesting

    7 }

  • 7/28/2019 Design Bootcamp

    121/144

    Entity, Value Object,

    and Values

    Category: Object Modeling

    Problem: In both cases, you want to encapsulate a set of data intoa 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.

  • 7/28/2019 Design Bootcamp

    122/144

    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

  • 7/28/2019 Design Bootcamp

    123/144

    Whats the Difference

    An Entityhas an identity and a value objectdoes not.

    Both are generally POPO's (Plain old PHP objects).

  • 7/28/2019 Design Bootcamp

    124/144

    Value Objects

    By definition, identity free and immutable.

    Valuesare simply put, any scalar in PHP (for all intents and

    purposes).

    Two separate Entitiescan share the same reference to a ValueObject.

  • 7/28/2019 Design Bootcamp

    125/144

    Entity1 class Artist {

    2 public $id; // has identity!

    3 public $name; // has identity!

    4 public $yearFormed;

    5 }

  • 7/28/2019 Design Bootcamp

    126/144

    The Classic Example In PHP

    ... Is not what you think it is.

    Traditionally, a DateTimeobject is generally considered a Value

    Object. Here is why it is not in PHP...

  • 7/28/2019 Design Bootcamp

    127/144

    Code sample PHP's DateTime1 class DateTime {

    2 public function modify(/* string */$modify);

    3 public function set*();

    4 public function add(DateInterval $interval);

    5 }

  • 7/28/2019 Design Bootcamp

    128/144

    Value Object1 /**

    2 * NOT PHP's DATETIME!!!!

    3 */

    4 class Date {

    5 public $year;

    6 public $month;

    7 public $day;

    8 }

  • 7/28/2019 Design Bootcamp

    129/144

    Value1 $artist = new Artist;

    2

    3 // name is a value, a string

    4 $artist->name = 'Splender';

  • 7/28/2019 Design Bootcamp

    130/144

    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?

  • 7/28/2019 Design Bootcamp

    131/144

  • 7/28/2019 Design Bootcamp

    132/144

    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

  • 7/28/2019 Design Bootcamp

    133/144

    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)

  • 7/28/2019 Design Bootcamp

    134/144

    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

  • 7/28/2019 Design Bootcamp

    135/144

    Exercise

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

  • 7/28/2019 Design Bootcamp

    136/144

    Base Application

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

  • 7/28/2019 Design Bootcamp

    137/144

    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 Albuminformation

    We might want to be able to pull information from web services

  • 7/28/2019 Design Bootcamp

    138/144

    The Domain Model

  • 7/28/2019 Design Bootcamp

    139/144

    Switch To IDE

    Time to switch to IDE, lets explore code

  • 7/28/2019 Design Bootcamp

    140/144

    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 2004Jason E. Sweat, Guide to PHP Design Patterns, Marco Tabini &

    Associates 2004

    Matt Zandstra , PHP Objects, Patterns and Practice, Apress (3

    edition) 2010

  • 7/28/2019 Design Bootcamp

    141/144

    References (2)

    Matthew Weier O'Phinney, Proxies in PHP

    Giorgio Sironi, Pratical PHP Patterns: Decorator

    PHP Manual, The Iterator Interface

  • 7/28/2019 Design Bootcamp

    142/144

    Resources

    https://github.com/ezimuel/PHP-design-patterns

    https://github.com/ralphschindler/PatternsTutorialApp

  • 7/28/2019 Design Bootcamp

    143/144

  • 7/28/2019 Design Bootcamp

    144/144

    Thank You