
Structural patterns
Structural patterns deal with class and object composition. Using interfaces or abstract classes and methods, they define ways to compose objects, which in turn obtain new functionality. The following is a list of patterns we categorize as structural patterns:
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
Note
See design patterns.
Adapter pattern
The adapter pattern allows the interface of an existing class to be used from another interface, basically, helping two incompatible interfaces to work together by converting the interface of one class into an interface expected by another class.
The following is an example of adapter pattern implementation:
class Stripe { public function capturePayment($amount) { /* Implementation... */ } public function authorizeOnlyPayment($amount) { /* Implementation... */ } public function cancelAmount($amount) { /* Implementation... */ } } interface PaymentService { public function capture($amount); public function authorize($amount); public function cancel($amount); } class StripePaymentServiceAdapter implements PaymentService { private $stripe; public function __construct(Stripe $stripe) { $this->stripe = $stripe; } public function capture($amount) { $this->stripe->capturePayment($amount); } public function authorize($amount) { $this->stripe->authorizeOnlyPayment($amount); } public function cancel($amount) { $this->stripe->cancelAmount($amount); } } // Client $stripe = new StripePaymentServiceAdapter(new Stripe()); $stripe->authorize(49.99); $stripe->capture(19.99); $stripe->cancel(9.99);
We started off by creating a concrete Stripe
class. We then defined the PaymentService
interface with some basic payment handling methods. The StripePaymentServiceAdapter
implements the PaymentService
interface, providing concrete implementation of payment handling methods. Finally, the client instantiates the StripePaymentServiceAdapter
and executes the payment handling methods.
Bridge pattern
The bridge pattern is used when we want to decouple a class or abstraction from its implementation, allowing them both to change independently. This is useful when the class and its implementation vary often.
The following is an example of bridge pattern implementation:
interface MailerInterface { public function setSender(MessagingInterface $sender); public function send($body); } abstract class Mailer implements MailerInterface { protected $sender; public function setSender(MessagingInterface $sender) { $this->sender = $sender; } } class PHPMailer extends Mailer { public function send($body) { $body .= "\n\n Sent from a phpmailer."; return $this->sender->send($body); } } class SwiftMailer extends Mailer { public function send($body) { $body .= "\n\n Sent from a SwiftMailer."; return $this->sender->send($body); } } interface MessagingInterface { public function send($body); } class TextMessage implements MessagingInterface { public function send($body) { echo 'TextMessage > send > $body: ' . $body; } } class HtmlMessage implements MessagingInterface { public function send($body) { echo 'HtmlMessage > send > $body: ' . $body; } } // Client $phpmailer = new PHPMailer(); $phpmailer->setSender(new TextMessage()); $phpmailer->send('Hi!'); $swiftMailer = new SwiftMailer(); $swiftMailer->setSender(new HtmlMessage()); $swiftMailer->send('Hello!');
We started off by creating a MailerInterface
. The concrete Mailer
class then implements the MailerInterface
, providing a base class for PHPMailer
and SwiftMailer
. We then define the MessagingInterface
, which gets implemented by the TextMessage
and HtmlMessage
classes. Finally, the client instantiates PHPMailer
and SwiftMailer
, passing on instances of TextMessage
and HtmlMessage
prior to calling the send
method.
Composite pattern
The composite pattern is about treating the hierarchy of objects as a single object, through a common interface. Where the objects are composed into three structures and the client is oblivious to changes in the underlying structure because it only consumes the common interface.
The following is an example of composite pattern implementation:
interface Graphic { public function draw(); } class CompositeGraphic implements Graphic { private $graphics = array(); public function add($graphic) { $objId = spl_object_hash($graphic); $this->graphics[$objId] = $graphic; } public function remove($graphic) { $objId = spl_object_hash($graphic); unset($this->graphics[$objId]); } public function draw() { foreach ($this->graphics as $graphic) { $graphic->draw(); } } } class Circle implements Graphic { public function draw() { echo 'draw-circle'; } } class Square implements Graphic { public function draw() { echo 'draw-square'; } } class Triangle implements Graphic { public function draw() { echo 'draw-triangle'; } } $circle = new Circle(); $square = new Square(); $triangle = new Triangle(); $compositeObj1 = new CompositeGraphic(); $compositeObj1->add($circle); $compositeObj1->add($triangle); $compositeObj1->draw(); $compositeObj2 = new CompositeGraphic(); $compositeObj2->add($circle); $compositeObj2->add($square); $compositeObj2->add($triangle); $compositeObj2->remove($circle); $compositeObj2->draw();
We started off by creating a Graphic
interface. We then created the CompositeGraphic
, Circle
, Square
, and Triangle
, all of which implement the Graphic
interface. Aside from just implementing the draw
method from the Graphic
interface, the CompositeGraphic
adds two more methods, used to track internal collection of graphics added to it. The client then instantiates all of these Graphic
classes, adding them all to the CompositeGraphic
, which then calls the draw
method.
Decorator pattern
The decorator pattern allows behavior to be added to an individual object instance, without affecting the behavior of other instances of the same class. We can define multiple decorators, where each adds new functionality.
The following is an example of decorator pattern implementation:
interface LoggerInterface { public function log($message); } class Logger implements LoggerInterface { public function log($message) { file_put_contents('app.log', $message, FILE_APPEND); } } abstract class LoggerDecorator implements LoggerInterface { protected $logger; public function __construct(Logger $logger) { $this->logger = $logger; } abstract public function log($message); } class ErrorLoggerDecorator extends LoggerDecorator { public function log($message) { $this->logger->log('ERROR: ' . $message); } } class WarningLoggerDecorator extends LoggerDecorator { public function log($message) { $this->logger->log('WARNING: ' . $message); } } class NoticeLoggerDecorator extends LoggerDecorator { public function log($message) { $this->logger->log('NOTICE: ' . $message); } } $logger = new Logger(); $logger->log('Resource not found.'); $logger = new Logger(); $logger = new ErrorLoggerDecorator($logger); $logger->log('Invalid user role.'); $logger = new Logger(); $logger = new WarningLoggerDecorator($logger); $logger->log('Missing address parameters.'); $logger = new Logger(); $logger = new NoticeLoggerDecorator($logger); $logger->log('Incorrect type provided.');
We started off by creating a LoggerInterface
, with a simple log
method. We then defined Logger
and LoggerDecorator
, both of which implement the LoggerInterface
. Followed by ErrorLoggerDecorator
, WarningLoggerDecorator
, and NoticeLoggerDecorator
which implement the LoggerDecorator
. Finally, the client part instantiates the logger
three times, passing it different decorators.
Facade pattern
The facade pattern is used when we want to simplify the complexities of large systems through a simpler interface. It does so by providing convenient methods for most common tasks, through a single wrapper class used by a client.
The following is an example of facade pattern implementation:
class Product { public function getQty() { // Implementation } } class QuickOrderFacade { private $product = null; private $orderQty = null; public function __construct($product, $orderQty) { $this->product = $product; $this->orderQty = $orderQty; } public function generateOrder() { if ($this->qtyCheck()) { $this->addToCart(); $this->calculateShipping(); $this->applyDiscount(); $this->placeOrder(); } } private function addToCart() { // Implementation... } private function qtyCheck() { if ($this->product->getQty() > $this->orderQty) { return true; } else { return true; } } private function calculateShipping() { // Implementation... } private function applyDiscount() { // Implementation... } private function placeOrder() { // Implementation... } } // Client $order = new QuickOrderFacade(new Product(), $qty); $order->generateOrder();
We started off by creating a Product
class, with a single getQty
method. We then created a QuickOrderFacade
class that accepts product
instance and quantity via a constructor
and further provides the generateOrder
method that aggregates all of the order generating actions. Finally, the client instantiates the product
, which it passes onto the instance of QuickOrderFacade
, calling the generateOrder
on it.
Flyweight pattern
The flyweight pattern is about performance and resource reduction, sharing as much data as possible between similar objects. What this means is that instances of a class which are identical are shared in an implementation. This works best when a large number of same class instances are expected to be created.
The following is an example of flyweight pattern implementation:
interface Shape { public function draw(); } class Circle implements Shape { private $colour; private $radius; public function __construct($colour) { $this->colour = $colour; } public function draw() { echo sprintf('Colour %s, radius %s.', $this->colour, $this->radius); } public function setRadius($radius) { $this->radius = $radius; } } class ShapeFactory { private $circleMap; public function getCircle($colour) { if (!isset($this->circleMap[$colour])) { $circle = new Circle($colour); $this->circleMap[$colour] = $circle; } return $this->circleMap[$colour]; } } // Client $shapeFactory = new ShapeFactory(); $circle = $shapeFactory->getCircle('yellow'); $circle->setRadius(10); $circle->draw(); $shapeFactory = new ShapeFactory(); $circle = $shapeFactory->getCircle('orange'); $circle->setRadius(15); $circle->draw(); $shapeFactory = new ShapeFactory(); $circle = $shapeFactory->getCircle('yellow'); $circle->setRadius(20); $circle->draw();
We started off by creating a Shape
interface, with a single draw
method. We then defined the Circle
class implementing the Shape
interface, followed by the ShapeFactory
class. Within the ShapeFactory
, the getCircle
method returns an instance of a new Circle
, based on the color
option. Finally, the client instantiates several ShapeFactory
objects, passing in different colors to the getCircle
method call.
Proxy pattern
The proxy design pattern functions as an interface to an original object behind the scenes. It can act as a simple forwarding wrapper or even provide additional functionality around the object it wraps. Examples of extra added functionality might be lazy loading or caching that might compensate for resource intense operations of an original object.
The following is an example of proxy pattern implementation:
interface ImageInterface { public function draw(); } class Image implements ImageInterface { private $file; public function __construct($file) { $this->file = $file; sleep(5); // Imagine resource intensive image load } public function draw() { echo 'image: ' . $this->file; } } class ProxyImage implements ImageInterface { private $image = null; private $file; public function __construct($file) { $this->file = $file; } public function draw() { if (is_null($this->image)) { $this->image = new Image($this->file); } $this->image->draw(); } } // Client $image = new Image('image.png'); // 5 seconds $image->draw(); $image = new ProxyImage('image.png'); // 0 seconds $image->draw();
We started off by creating an ImageInterface
, with a single draw
method. We then defined the Image
and ProxyImage
classes, both of which extend the ImageInterface
. Within the __construct
of the Image
class, we simulated the resource intense operation with the sleep
method call. Finally, the client instantiates both Image
and ProxyImage
, showing the execution time difference between the two.