Design Patterns Workshop Brandon Savage Who am I?

• Former Mozillian; teacher, writer, speaker. • Author of Mastering Object Oriented PHP and Practical in PHP. • Instrument rated private pilot. What is a design pattern? A design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. That doesn't seem so hard, right? ...general[ly] reusable solution...... commonly occurring problem... Design patterns are abstract! You're probably using design patterns without knowing it. What is NOT a design pattern? A design pattern is not a finished design that can be transformed directly into source or machine code.

It is a description or template for how to solve a problem that can be used in many different situations. One pattern can have 1,000 implementations! Patterns are formalized best practices that the programmer must implement themselves in the application.

Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. Design patterns are often expressed in diagrams, not code. Code Sample Warning!!!

Code is provided to ILLUSTRATE possible solutions, not to show all possible solutions. Developing a SOLID understanding Design patterns often implement OO best practices. S ingle Responsibility O pen/Closed L iskov substitution I nterface Segregation D ependency injection SOLID helps us develop good object oriented applications. Single responsibility The single responsibility principle states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility. One class, one job. Examples of a job

• Database connection handling • Caching • Object instantiation • Routing • Context management • Data modeling ...entirely encapsulated... The object should be able to do the job completely. Open/Closed Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. Two kinds of open/ closed principle Meyer's open/closed principle Meyer's open/closed principle • Classes, once complete, should not be modified. • Errors can be fixed, but the API and internals are set • Inheritance allows for future modifications and extensions • Subclasses CAN change the interface. Polymorphic Open/ Closed Principle Polymorphism is the natural occurrence of a thing in different forms. ! In computer science, this means an object that works in different ways, but implements a common interface and behavior. Polymorphic Open/ Closed Principle

• The INTERFACE defines the object. • The internals are open for extension, but the interface closed for modification. • Changing the interface in subclasses changes the object type. Most developers use the Polymorphic Open/ Closed Principle. A Caching Interface

A File Cache

path . '/' . $key); } ! public function set($key, $value) { return file_put_contents($this->path . '/' . $key, $value); }

public function delete($key) { $path = $this->path . '/' . $key; unset($path); } ! // ...other methods here } An APC Cache

ALL instances of Cache should be substitutable for one another! Do not extend the interface (open/closed violation)

compareImageChannels ( $image, $channelType, $metricType); compareImageLayers ($method); compareImages ( $compare, $metric); compositeImage ($composite_object, $composite, $x, $y $channel = Imagick::CHANNEL_ALL ); __construct ( $files); contrastImage ( $sharpen); contrastStretchImage ($black_po, $white_po, $channel = Imagick::CHANNEL_ALL ); convolveImage (array $kernel, $channel = Imagick::CHANNEL_ALL ); cropImage ($width, $height, $x, $y); cropThumbnailImage ($width, $height); current() cycleColormapImage ($displace); decipherImage (string $passphrase); deconstructImages (); deleteImageArtifact (string $artifact); deskewImage ($threshold); despeckleImage (); destroy (); displayImage (string $servername); displayImages (string $servername); distortImage ($method, array $arguments, $bestfit); drawImage (ImagickDraw $draw); edgeImage ($radius); embossImage ($radius, $sigma); encipherImage (string $passphrase); ...and going... enhanceImage (); equalizeImage (); evaluateImage ($op, $constant $channel = Imagick::CHANNEL_ALL ); exportImagePixels ($x, $y, $width, $height, string $map, $STORAGE); extentImage ($width, $height, $x, $y); flattenImages (); flipImage (); floodFillPaImage ( $fill, $fuzz, $target, $x, $y, $invert, $channel = Imagick::CHANNEL_DEFAULT ); flopImage (); frameImage ( $matte_color, $width, $height, $inner_bevel, $outer_bevel); functionImage ($function, array $arguments, $channel = Imagick::CHANNEL_DEFAULT ); fxImage (string $expression $channel = Imagick::CHANNEL_ALL ); gammaImage ($gamma $channel = Imagick::CHANNEL_ALL ); gaussianBlurImage ($radius, $sigma $channel = Imagick::CHANNEL_ALL ); getColorspace (); getCompression (); getCompressionQuality (); getCopyright (); getFilename (); getFont (); getFormat (); getGravity (); getHomeURL (); getImage (); ...and going still...

! getImageAlphaChannel (); getImageArtifact (string $artifact); getImageBackgroundColor (); getImageBlob (); getImageBluePrimary (); getImageBorderColor (); getImageChannelDepth ($channel); getImageChannelDistortion ( $reference, $channel, $metric); getImageChannelDistortions ( $reference, $metric $channel = Imagick::CHANNEL_DEFAULT ); getImageChannelExtrema ($channel); getImageChannelKurtosis ([ $channel = Imagick::CHANNEL_DEFAULT ); getImageChannelMean ($channel); getImageChannelRange ($channel); getImageChannelStatistics (); getImageClipMask (); getImageColormapColor ($index); getImageColors (); getImageColorspace (); getImageCompose (); getImageCompression (); getCompressionQuality (); getImageDelay (); getImageDepth (); getImageDispose (); Dependency inversion Many people think of this as the "" principle. It's not. The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Also, abstractions should not depend on details; details should depend on abstractions. Type hinting When we type hint, we are relying on a specific object type. ! Even though PHP will let us hint for a class name, we should be hinting for interfaces, not class names. By relying on abstractions, we free our application from dependency hell. We also make testing possible! You DO write tests, don't you? Service location is not dependency inversion. NOT dependency inversion.

find($id); ! if (!$profile) return Rest::notFound(); ! if ($profile->delete()) { return Rest::okay([]); } else { return Rest::conflict(); } } ! Design patterns we will cover • Decorator • Mediator • Abstract Factory & Builder • Singleton • Adapter & Facade • Chain of Responsibility • Strategy • Observer The Composition over inheritance. Inheritance is great.

! But it has limitations. PHP doesn't support multiple inheritance! String Decorator

public function printString();

} ! class StringPrinter implements Printer {

protected $string;

public function __construct($string) { $this->string = $string; }

public function printString() { print($this->string); }

} Strips HTML

protected $printer;

public function __construct(Printer $printer) { $this->printer = $printer; }

public function printString() { ob_start(); $this->printer->printString(); $string = ob_get_clean(); print strip_tags($string); } } Makes Text Bold

protected $printer;

public function __construct(Printer $printer) { $this->printer = $printer; }

public function printString() { print ''; $this->printer->printString(); print ''; } } All Together Now

hello world'); $a->printString(); //
hello world ! $a = new StripTagsPrinter(new StringPrinter('
hello world')); $a->printString(); // hello world ! $a = new BoldPrinter(new StripTagsPrinter(new StringPrinter('
hello world'))); $a->printString(); // hello world

Decorating With Traits Traits introduced in PHP 5.4 Traits are expressed in objects (they are not part of an object's type) The Interface and Traits

public function printString();

} ! trait StripTags {

public function stripTags() { $this->string = strip_tags($this->string); } } ! trait BoldString {

public function boldString() { $this->string = '' . $this->string . ''; } } String Printer

class PrintString implements Printer {

protected $string;

public function __construct($string) { $this->string = $string; }

public function printString() { print $this->string; }

} Bold String Printer

class BoldStringPrinter implements Printer {

use BoldString;

protected $string;

public function __construct($string) { $this->string = $string; }

public function printString() { $this->boldString(); print $this->string; } } Strip Tags/Bold Printer

class BoldNoHtmlStringPrinter implements Printer { use StripTags, BoldString;

protected $string;

public function __construct($string) { $this->string = $string; }

public function printString() { $this->stripTags(); $this->boldString(); print $this->string; } } All Together Now

$a = new PrintString('
hello world'); $a->printString(); //
hello world ! $a = new BoldStringPrinter('
hello world'); $a->printString(); //
hello world ! $a = new BoldNoHtmlStringPrinter('
hello world'); $a->printString(); // hello world OCP/LSP Violation? OCP/LSP Violation?

protected $printer;

public function __construct(Printer $printer) { $this->printer = $printer; }

public function printString() { print ''; $this->printer->printString(); print ''; } } Magic methods are not "part of the interface." The An object between friends Problem: you have too many objects.

Many objects talking to each other introduces tight coupling. Coupling is a measure of the interdependency between objects in a particular application. ! Tight coupling exists when an object is dependent on many other objects, which are in turn dependent upon it. Tight coupling reduces testability and reusability while increasing the cost of maintenance. Solution: the Mediator

The Mediator Pattern

Mediator

Object 1 Object 2 Domain Model

Gateway

Value Obj Storage The Mediator Pattern

valueObj->getValues(); return $this->dataObj->storeValues($values); } }

The Mediator Pattern is great for loose coupling, because it discourages modules from talking to one another. Abstract Factory and Builder Abstracting object creation Creating objects is a responsibility. To abstract the object creation responsibility, we use patterns. Abstract Factory Simple objects, part of a family, with limited configuration

The client only cares about two interfaces: CacheFactory and Cache. Abstract factories can only produce simple objects. Abstract factories always return the object on creation. The Managing a more complex creation process Some objects are more complex and require more effort to construct.

Just like Abstract Factory, the Client only knows two interfaces. The client manages the building process The created object is only returned when the Client asks for it. The Builder Pattern

if(!$this->request->getUser()->isAuthenticated()) { $this->response->setRedirect('/'); return $this->response->render(); }

$this->response->setTemplate('account.html'); $this->response->setTitle('My Account'); return $this->response->render(); } } The Builder Pattern

public function setTemplate($template);

public function setTitle($title);

public function render(); ! } The Builder Pattern class ResponseBuilder implements AbstractResponseBuilder{ ! protected $product; ! public function setRedirect($url) { $this->product = new 301RedirectResponse($url); }

public function setTemplate($template) { if(!($this->product instanceof 200OKResponse)) { $this->product = new 200OKResponse; }

$this->product->setTemplate($template); }

public function setTitle($title) { if(!($this->product instanceof 200OKResponse)) { $this->product = new 200OKResponse; }

$this->product->setTitle($title); }

public function render() { if(!is_object($this->product)) { throw new InvalidResponseException(); }

return $this->product; } } Using these patterns together Together again

class PostgresOrmFactory implements AbstractORM { ! public function getInstance() { return new PostgresORM(); } } ! interface ORMBuilder { ! public function setTable($table); public function setColumns(array $columns = array()); public function setWhere(array $where = array()); public function setOrderBy($orderBy, $ascDesc); public function setLimit($limit, start); public function addJoin($rawSQL);

// and so on... ! } The Singleton Pattern The pattern we love to hate A Singleton is a single instance of a particular object within a particular runtime. The Singleton

return self::$instance; } ! } The Singleton and SOLID The Singleton and SOLID

return self::$instance; } ! } The Singleton and Testing The Singleton and Testing

return self::$instance; } ! } The Singleton has uses!

! They're just few and far between. The Adapter and the Facade Patterns Two similar patterns, two very different reasons Problem: we don't write all the code we use. A lot of times, very similar components look very different. The Making two different interfaces look the same (You've probably implemented this before) A Caching Interface

A File Cache

path . '/' . $key); } ! public function set($key, $value) { return file_put_contents($this->path . '/' . $key, $value); }

public function delete($key) { $path = $this->path . '/' . $key; unset($path); } ! // ...other methods here } An APC Cache

The Adapter Pattern and LSP The Adapter Pattern is NOT the Mediator Pattern! Mediators mediate between colleagues. Adapters translate between interfaces. The Complex subsystem, simple interface Problem: you have a complex subsystem that a higher level system must access.

Facades often tie together unrelated subcomponents. Facades create an interface where one didn't exist before. WARNING: “Facades” are not implementing this pattern and are poorly named. Violation of SRP How is a Facade NOT An Adapter?! Adapters create a common interface for SIMILAR objects.

! Facades create interfaces for UNRELATED objects. Some patterns look similar, but are very different based on intent. The Chain of Responsibility Pattern Handling things in order Problem: you need to be able to handle tasks or filters in a particular order.

Each object in the chain either handles the request, or passes it along. The Chain Interface

protected $successor;

public function appointSuccessor(Handler $successor) { $this->successor = $successor; } ! protected function passMessage(EmailMessage $message) { if($successor instanceof Handler) { return $this->successor->handleMessage($message); } }

abstract public function handleMessage( EmailMessage $message);

} Wait, you used an abstract class, not an interface! Abstract Classes ARE Interfaces. The Chain Interface

protected $successor;

public function appointSuccessor(Handler $successor) { $this->successor = $successor; } ! protected function passMessage(EmailMessage $message) { if($successor instanceof Handler) { return $this->successor->handleMessage($message); } }

abstract public function handleMessage( EmailMessage $message);

} The Chain Interface

protected $successor;

public function appointSuccessor(Handler $successor) { $this->successor = $successor; } ! protected function passMessage(EmailMessage $message) { if($successor instanceof Handler) { return $this->successor->handleMessage($message); } }

abstract public function handleMessage( EmailMessage $message);

} The End Of The Chain; Now What Do We Do? Falling off the chain is bad. Usually. Option 1: Raise An Exception. The Chain Interface

protected $successor;

public function appointSuccessor(Handler $successor) { $this->successor = $successor; } ! protected function passMessage(EmailMessage $message) { if($successor instanceof Handler) { return $this->successor->handleMessage($message); }

throw new HandlerNotFoundException( 'I could not find a handler.'); }

abstract public function handleMessage( EmailMessage $message); } Option 2: Raise a 404 (in a router context) Option 3: Fall Off (Failure By Design) Falling off is okay, if that's expected. Construction of the chain is crucial! E.g. a greedy regex can keep the chain from working right. The Determining your approach at runtime Problem: Depending on context, we may want to switch how we execute. We also know that composition is better than inheritance. The Strategy Pattern

} ! class Subtract implements Strategy { ! public function execute($a, $b) { return $a - $b; }

} ! class Multiply implements Strategy { ! public function execute($a, $b) { return $a * $b; }

} The Strategy Pattern

strategy = $strategy; }

public function executeStrategy($a, $b) { return $this->strategy->execute($a, $b); } ! } The Strategy Pattern relies on a common interface. The Strategy Pattern

Observer Pattern Watching other objects Problem: one object should change when another changes state.

An Observer Interface

public function unregisterObserver(Observer $subscriber); ! public function notifyObservers($message); ! } ! interface Observer {

public function notify($message); ! } Avoiding a Push/Pull System A push system: the Subject determines what the Observer wants. This requires the Subject to know the Observer. A pull system: the Subject notifies the Observer of a change, and the Observer interrogates the Subject for details. This requires the Observer to know the Subject, and have a reference to it. Subjects should tell Observers EVERYTHING. Observers decide what they care about. Subjects hold references to Observers. Take your object oriented programming to the next level.

A brand new book on design patterns, with examples written in PHP for web developers! ! 10% off with code VERONA

http://www.practicaldesignpatternsinphp.com/ Questions?

[email protected] • www.brandonsavage.net • @brandonsavage