A - Decorator Pattern
A.1 - Use Case of Decorator Pattern
Decorator pattern is used for extending a legacy functionality without changing the legacy class. Let's say, we have a concrete class that implements an interface. And we need to extend the functionality of the existing method however because that the existing class, and its methods are already used by other classes, thus we don't want to make a change in the existing classes. But we also need extended functionality on newer class, then how do we solve this problem?
1- We can't change the existing legacy code
2- We want to extend the functionality
So we use decorator pattern, wrap the existing class inside the decorators.
B - Basic GoF Decorator Pattern Example
Here we have a simple interface and an implementation/concrete class. The interface has one simple method, which is getMessageOfTheDay
and it returns a String
. Assume that there are lots of other classes using this method. So if we want to make a change in the implementation/concrete class, it will affect the old legacy code. We want to change it for only the new classes so we use the decorator pattern.
Here is a trivial example of Gang Of Four Decorator Design pattern;
B.1 - Greeter.java
public interface Greeter {
String getMessageOfTheDay();
}
B.2 - BasicGreeter.java
public class BasicGreeter implements Greeter {
@Override
public String getMessageOfTheDay() {
return "Welcome to my server";
}
}
B.3 - Abstract Decorator Class: GreeterDecorator.java
public abstract class GreeterDecorator implements Greeter {
protected Greeter greeter;
public GreeterDecorator(Greeter greeter) {
this.greeter = greeter;
}
public String getMessageOfTheDay() {
return greeter.getMessageOfTheDay();
}
}
B.4 - Concrete Decorator Class: StrangerDecorator.java
public class StrangerDecorator extends GreeterDecorator {
public StrangerDecorator(Greeter greeter) {
super(greeter);
}
@Override
public String getMessageOfTheDay() {
return "Hello Stranger " + super.getMessageOfTheDay();
}
}
B.5 - Demo Code: DecoratorDemo .java
public class DecoratorDemo {
public static void main(String[] args) {
Greeter greeter = new BasicGreeter();
String motd = greeter.getMessageOfTheDay();
System.out.println(motd);
Greeter newGreeter = new StrangerDecorator(greeter);
String newMotd = newGreeter.getMessageOfTheDay();
System.out.println(newMotd);
Greeter muchNewGreeter = new StrangerDecorator(new StrangerDecorator(greeter));
String newestMotd = muchNewGreeter.getMessageOfTheDay();
System.out.println(newestMotd);
}
}
Take a look at those examples. The abstract decorator class is needed to wrap the original contract and implementation. Using the abstract decorator, you can create newer multiple decorators but in this example, BasicGreeter is wrapped inside the abstract decorator and we have only created on new decorator class which is StrangeGreeter. Please notify that decorator classes can be used like a train, we can wrap a decorator inside another decorator or the same. The functionality is extendable but the original class is preserved without any modification.
C - OutputStream Demo
Let's take a look at this example. We want to write a string to file with OutputStream. Here is the demo code;
C.1 - Sample OutputStream Demo To Write A File
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
File file = new File("./normal.txt");
file.createNewFile();
OutputStream oStream = new FileOutputStream(file);
String content = "I love Commodore 64";
oStream.write(content.getBytes());
oStream.close();
}
}
C.2 - JSON Decorator Output: normal.txt
There will be a new file with name "normal.txt" created under the project folder and the content will be;
I love Commodore 64
D - JSON OutputStream Decorator Demo
Now, I want to create a JSON wrapper format, which is as follows;
{
data: <data here>
}
What I want is to write the content inside a simple one field JSON format. How can we achieve this goal? There are many trivial ways. However, I will use the GoF Decorator Pattern by writing a JSONDecorator which extends the OutputStream class of Java;
D.1 - JSON Decorator for OutputStream: JSONStream.java
public class JSONStream extends OutputStream {
protected OutputStream outputStream;
public JSONStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void write(int b) throws IOException {
outputStream.write(b);
}
@Override
public void write(byte[] b) throws IOException {
String content = new String(b);
content = "{\r\n\tdata:\"" + content + "\"\r\n}";
outputStream.write(content.getBytes());
}
}
D.2 - JSON Decorator Demo: JSONDecoratorDemo.java
public class JSONDecoratorDemo {
public static void main(String[] args) throws IOException {
File file = new File("./json.txt");
file.createNewFile();
OutputStream oStream = new FileOutputStream(file);
JSONStream js = new JSONStream(oStream);
String content = "I love Commodore 64";
js.write(content.getBytes());
js.close();
oStream.close();
}
}
D.3 - JSON Decorator Output: json.txt
{
data:"I love Commodore 64"
}
Actually, OutputStream itself a Decorator Pattern, it is the abstract decorator and concrete decorator in here is the JSONStream class.