Upload
skmetaly
View
230
Download
0
Embed Size (px)
Citation preview
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