Decorator Pattern

The Decorator allow us to attach additional responsibilities to an object. The concept behind this pattern is not difficult to understand, as long as we have already understood other fundamental concepts as inheritance and composition.

Let’s start simple

A decorator is like a shell. In objectland this translates to an object that holds another object inside.

When we want to “talk” with the inner object, first we “talk” with the outer object, which acts as an intermediary. The outer object will then pass the message to the inner object.

By using this “middleman” technique, we can intercept the messages that are sent to, or sent by, the inner object. If we can intercept them, it becomes obvious that we can manipulate them according to our needs.

Can a Decorator decorate a Decorator ?

It surely can, and that’s when things become interesting.

An image will help to illustrate,

Initial situation :

Assembled situation :

In a situation as the one in the image, when we want to reach object X, we first need to communicate with object C (decorator C), which will pass the message to object B (decorator B), which will pass the message to object A (decorator A), and finally it will arrive to object X. Any reply from the object X, will follow the inverse path, and along the way we can manipulate the “conversation”

How do we build this structure ?

To achieve this object structure, we will have to make sure that the object and the decorators are all of the same type. An Interface will help us on that.

As all the objects are of the same type, we can “communicate” with the outer object as if we where dealing with the most inner object.

Our example

We’ve just started our internet text messaging company. We provide our users with 3 service plans. A free one, that allows them to send plain text messages, a 1 dollar plan that allows them to send encrypted messages and a 2 dollar plan that will add an extra encryption layer to the messages.

In our example, the simple encryption is represented by reversing the message, and the extra security layer is represented by codding the reversed message in Base64. This “highly sophisticated encryption” systems :) will be implemented in a class named Enigma.

This example can illustrate the way we can play with decorators, combining them to achieve different results.

The UML for our example,

We will implement a Message interface, which will allow us to group our objects under the same type. We will only need one method for this interface that will be sendMessage()

Message.java
1
2
3
4
5
6
package DecoratorPattern.ExampleTWO;

public interface Message {

void sendMessage(String message);
}

Now we will implement the MessageDecorator, as an abstract class. All other decorators will derive from this class by extending it, and the behaviour will be defined in each decorator subclass.

Message.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package DecoratorPattern.MessageExample;

public abstract class MessageDecorator implements Message {

protected Message decoratedMessage;


public MessageDecorator(Message message) {

this.decoratedMessage = message;
}

public abstract void sendMessage(String message);


public Message getDecoratedMessage() {

return this.decoratedMessage;
}
}

Next, we will implement our classes. One for each type of message.

The plain text message StandardMessage Class, will act as the base object ( the one that will be decorated according to our needs). This base class will implement the Message Interface directly.

StandardMessage.java
1
2
3
4
5
6
7
8
9
10
package DecoratorPattern.MessageExample;

public class StandardMessage implements Message {

@Override
public void sendMessage(String message) {
System.out.println("Messag to send :");
System.out.println(message + "\n");
}
}

And now, we implement the decorators which will be the ReversedMessage and EncriptedMessage classes. Both will extend MessageDecorator and by being subclasses, they will have access to parent’s methods.

ReversedMessage.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DecoratorPattern.MessageExample;

public class ReversedMessage extends MessageDecorator {

public ReversedMessage(Message message) {
super(message);
}

@Override
public void sendMessage(String message) {
Enigma enigma = new Enigma();
String reversedMessage = enigma.reverser(message);
this.getDecoratedMessage().sendMessage(reversedMessage);
}
}
EncriptedMessagejava
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package DecoratorPattern.MessageExample;

public class EncriptedMessage extends MessageDecorator {

public EncriptedMessage(Message message) {
super(message);
}

@Override
public void sendMessage(String message) {
Enigma enigma = new Enigma();
String enigmatedMessage = enigma.B64Encode(message);
this.getDecoratedMessage().sendMessage(enigmatedMessage);
}
}

Finally we implement the Enigma class, which is a helper class that takes care of the our “super secure” encryption system. :)

EncriptedMessagejava
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package DecoratorPattern.MessageExample;

import java.util.Base64;

public class Enigma {

/**
* codes the message in B64
*/
public String B64Encode(String messageToEncrypt) {

byte[] bytesEncoded = Base64.getEncoder().encode(messageToEncrypt.getBytes();
return new String(bytesEncoded);
}

/**
* Inverts a string
* abc -> cba
*/
public String reverser(String messageToReverse) {

return new StringBuilder(messageToReverse).reverse().toString();
}
}

Let’s play

With our classes in place, we can now start to play with them and by combining decorators we can achieve interesting results.

Sending a plain text message is straightforward. All we need to do is to instantiate a new StandardMessage and call the sendMessage method.

1
2
3
String messageToSend = "This is an important message about the decorator pattern";
Message message = new StandardMessage();
message.sendMessage(messageToSend);

When we want to decorate a message, is just a question of instantiating the kind of message we want to send, passing as argument either the base object or another decorator.

So, if we want to send a Base64 encrypted message ,

1
2
3
String messageToSend = "This is an important message about the decorator pattern";
Message encodedMessage = new EncriptedMessage(new StandardMessage());
encodedMessage.sendMessage(messageToSend);

and for a reversed message,

1
2
3
String messageToSend = "This is an important message about the decorator pattern";
Message reversedMessage = new ReversedMessage(new StandardMessage());
reversedMessage.sendMessage(messageToSend);

In this case we can play even further. If we want to send an Base64 encrypted reversed message, we can instantiate an EncriptedMessage passing as argument a ReversedMessage with a StandardMessage as argument.

1
2
3
String messageToSend = "This is an important message about the decorator pattern";
Message encodedAndReversedMessage = new EncriptedMessage(new ReversedMessage(new StandardMessage()));
encodedAndReversedMessage.sendMessage(messageToSend);

We can do another combination, reversing the message first and Base64 encoding it next, and that would become,

1
2
3
String messageToSend = "This is an important message about the decorator pattern";
Message reversedAndEncoded = new ReversedMessage(new EncriptedMessage(new StandardMessage()));
reversedAndEncoded.sendMessage(messageToSend);

We can even be crazy enough to double reverse a message, although that is useless, as we will get the initial message, but in an experiment there is nothing like being crazy,

1
2
3
String messageToSend = "This is an important message about the decorator pattern";
Message doubleReversed = new ReversedMessage(new ReversedMessage(new StandardMessage()));
doubleReversed.sendMessage(messageToSend);

And this are the basics of the Decorator pattern. A very interesting alternative to sub-classing, that allow us to combine things in very creative ways.

An example for the code in this article can be found in git-hub

Happy coding.

Share