Design Patterns - Decorator
https://code.tutsplus.com/articles/a-beginners-guide-to-design-patterns--net-12752 - done reading
// Design Patterns - Structural - Decorator:
Decorator pattern allows a user to add new functionality to an existing object
without altering its structure. This type of design pattern comes under
structural pattern as this pattern acts as a wrapper to existing class.
This pattern creates a decorator class which wraps the original class and
provides additional functionality keeping class methods signature intact.
Add responsibilities to objects dynamically. Easy to add behavior at runtime.
The decorator pattern is a structural design pattern which enables us to add
new or additional behavior to an object during runtime, depending on the
situation. The goal is to make it so that the extended functions can be
applied to one specific instance, and, at the same time, still be able to
create an original instance that doesn't have the new functions. It also allows
for combining multiple decorators for one instance, so that you're not stuck
with one decorator for each instance. This pattern is an alternative to
subclassing, which refers to creating a class that inherits functionality from
a parent class. As opposed to subclassing, which adds the behavior at compile
time, "decorating" allows you to add new behavior during runtime, if the
situation calls for it.
To implement the decorator pattern, we can follow these steps:
1. Subclass the original "Component" class into a "Decorator" class
2. In the Decorator class, add a Component pointer as a field
3. Pass a Component to the Decorator constructor to initialize the Component
pointer
4. In the Decorator class, redirect all "Component" methods to the "Component"
pointer, and
5. In the Decorator class, override any Component method(s) whose behavior needs
to be modified.
// Step 1: Create an interface.
public interface Shape {
void draw();
}
// Step 2: Create concrete classes implementing the same interface.
public class Rectangle implements Shape {
public void draw() {
System.out.println("Shape: Rectangle");
}
}
public class Circle implements Shape {
public void draw() {
System.out.println("Shape: Circle");
}
}
// Step 3: Create abstract decorator class implementing the Shape interface.
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
// Step 4: Create concrete decorator class extending the ShapeDecorator class.
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
// Step 5: Use the RedShapeDecorator to decorate Shape objects
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
In the above code, we instantiate two instances of the RedShapeDecorator class,
one instance wraps around the Circle object, and the other instance wraps
around the Rectangle object. The draw method of the RedShapeDecorator class
forward the call to the wrapped object, but add additional functionality either
before or after the call.
The best place to use the decorator pattern is when you have an entity which
needs to have new behavior only if the situation requires it. Let's say you
have an HTML link element, a logout link, that you want to do slightly different
things to based on the current page. For that, we can use the decorator pattern.
First, let's establish the different "decorations" we'll need:
1. If we're on the home page and logged in, have this link be wrapped in h2 tags
2. If we're on a different page and logged in, have this link be wrapped in
underline tags.
3. If we're logged in, have this link wrapped in strong tags
<?php
class HtmlLinks {
//some methods which is available to all html links
}
class LogoutLink extends HtmlLinks {
protected $_html;
public function __construct() {
$this->_html = "<a href=\"logout.php\">Logout</a>";
}
public function setHtml($html)
{
$this->_html = $html;
}
public function render()
{
echo $this->_html;
}
}
class LogoutLinkH2Decorator extends HtmlLinks {
protected $_logout_link;
public function __construct( $logout_link )
{
$this->_logout_link = $logout_link;
$this->setHtml("<h2>" . $this->_html . "</h2>");
}
public function __call( $name, $args )
{
$this->_logout_link->$name($args[0]);
}
}
class LogoutLinkUnderlineDecorator extends HtmlLinks {
protected $_logout_link;
public function __construct( $logout_link )
{
$this->_logout_link = $logout_link;
$this->setHtml("<u>" . $this->_html . "</u>");
}
public function __call( $name, $args )
{
$this->_logout_link->$name($args[0]);
}
}
class LogoutLinkStrongDecorator extends HtmlLinks {
protected $_logout_link;
public function __construct( $logout_link )
{
$this->_logout_link = $logout_link;
$this->setHtml("<strong>" . $this->_html . "</strong>");
}
public function __call( $name, $args )
{
$this->_logout_link->$name($args[0]);
}
}
We should then be able to use it like so:
$logout_link = new LogoutLink();
if( $is_logged_in ) {
$logout_link = new LogoutLinkStrongDecorator($logout_link);
}
if( $in_home_page ) {
$logout_link = new LogoutLinkH2Decorator($logout_link);
} else {
$logout_link = new LogoutLinkUnderlineDecorator($logout_link);
}
$logout_link->render();
We can see here how we are able to combine multiple decorators if we need them.
Since all the decorators use the __call magic function, we can still call the
original function's methods.
In the above code, we have 5 classes:
1. HtmlLinks
2. LogoutLink
3. LogoutLinkH2Decorator
4. LogoutLinkUnderlineDecorator
5. LogoutLinkStrongDecorator
The first two classes (HtmlLinks and LogoutLink) are base classes.
There is nothing special about them. The other classes (LogoutLinkH2Decorator,
LogoutLinkUnderlineDecorator, LogoutLinkStrongDecorator) all have a member
variable name $_logout_link, and their constructor function all take a object
of the LogoutLink class. These classes keep this object in the $_logout_link
member variable. These classes use the magic @@__call@@ function to forward
the invocation to the original object.
This pattern creates a decorator class which wraps the original class and
provides additional functionality keeping class methods signature intact.
page revision: 8, last edited: 16 Nov 2016 01:50