Decorator pattern drib design pattern series, part #1

Scroll this

What is design pattern

In software engineering, a software design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. It 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. Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system.

source: wikipedia

Decorator pattern

Changes behavior, adds functionality to an individual objects, usually at runtime (dynamically).This means that it doesn’t affect other objects of that class. Also, usually it doesn’t change the interface of the object. Relationship between the Decorator class and class which it decorates is both HAS_A and IS_A (or if not IS_A, at least it implements the same interface – point being that decorator can be drop in replacement for the “original” object).

Example

Description

Lets decorate an existing class which sends some text over the network, to the log file, or just prints on the screen – not important for this tutorial. Functionality like sending compressed text, obfuscate text, etc… will be implemented by using Decorator pattern.

Class which we will decorate is MessageWritter and it implements interface MessageWritterInterface. Put them into message subdirectory.

message/messagewritterinterface.php

message/messagewritter.php

Example of  MessageWriter usage:

main.php

Save main.php outside of the message subdirectory.

Output:

Nothing spectacular for now. Lets write the first decorator:

message/gzcompressmessagewriterdecorator.php

Please notice a HAS_A relationship since the $_messageWriter is a member variable which contains object which implements MessageWriterInterface. Decorator also implements MessageWriterInterface – this is kind of IS_A relationship since decorator can be drop in replacement anywhere where  MessageWriterInterface is expected.

Decorator implementation of the writeText compresses the text before it is outputted by the “original” object.

Add the following lines to the main.php:

Output:

So here it is: functionality to output (or send compressed) text was added at the runtime.

But now, we might get into problems, what if compressed text is to be transported over text only media? Lets write base64 decorator:

message/base64messagewriterdecorator.php

Please remember that Decorator wraps around any object that implements MessageWriterInterfaceso it can wrap around the original object MessageWritter or any MessageWritter decorators. We can chain decorators!

Add the following lines to the main.php:

Output:

In the example above text is first compressed (since GzCompressMessageWriterDecorator::writeText() ) is executed first. Then it is converted to the base64 (since Base64MessageWriterDecorator::writeText() is executed second) and finally the text is outputted by MessageWritter::writeText() .

Technically,  we can chain decorators in any order, but we will not get the desired result if we order them in a wrong way. For example this:

would first convert text to base64 and then compress it – but there is no point of doing this.

Please note another advantage – since decorators can be chained – we can have decorators that do one thing and then combined them in any way we wish. From example above: it is enough that we have gzcompress decorator and base64 decorator, we don’t need a third decorator which will implement gzcompress and base64.

Refactoring

There is fragment of the code which appears in both decorators:

This code can be put into MessageWriterDecorator class, which will be extended by other decorator classes, so there will be no code duplication:

message/messagewritterdecorator.php

Update the  Base64MessageWriterDecorator and  GzCompressMessageWriterDecorator to inherit from MessageWriterDecorator. This is their updated version:

message/gzcompressmessagewriterdecorator.php

message/base64messagewriterdecorator.php

Just for fun here is the third l33t decorator:

message/leetmessagewriterdecorator.php

Add the following lines to the main.php:

Output:

Add the following lines to the main.php:

Base64 l33t output – because – why not? 😉

Complete main.php:

Issues and alternatives

If you are decorating object with lot of public methods you are forced to delegate all of them to the decorated object. In other words, you need to write wrapper methods for each public method which will only call decorated object method. Your decorator will be full of code like this:

etc… Which is just too much useless work. Unfortunately you can’t use PHP magic methods like __call since you are implementing interface and all methods must be implemented.

Alternatively you can make decorator not be associated with the decorated class in any way: it doesn’t implement the same interface, it doesn’t extend the same abstract class, etc… . In that case you can use magic methods for methods and properties which you don’t want to decorate.  Since PHP is loosely typed language this will work. But you will loose the possibility to use type declarations (previously known as type hinting) – which can be very helpful in the larger projects.

References

Submit a comment