There are some situations in which we need high flexibility. Let’s consider that we want to create a flexible code for mixing a lot of different pizzas. We would not want to write all the possibilities without a pattern, right? For this reason, we want to use the Decorator pattern to gain flexibility, high cohesion, and low coupling.
Get the Design_Patterns_Saga_GitHub_Source_Code
1 – Generic interface (Component): It’s the interface with the method that will create all of the objects’ decorations.
public interface Pizza { public String make(); }
2 – Decorator: This is the class that makes things happen. It is on the top of all decorator classes and is responsible for decorating the objects.
public abstract class PizzaDecorator implements Pizza { protected Pizza customPizza; public PizzaDecorator(Pizza customPizza) { this.customPizza = customPizza; } public String make() { return customPizza.make(); } }
3 – Decorator classes: These are the classes that will decorate the final pizza. Basically, we will decorate the pizza with the toppings we want in a flexible way.
public class TuscanyDecorator extends PizzaDecorator { public TuscanyDecorator(Pizza customPizza) { super(customPizza); } public String make() { return customPizza.make() + addTuscany(); } private String addTuscany() { return " + Tuscany"; } } public class MozzarellaDecorator extends PizzaDecorator { public MozzarellaDecorator(Pizza customPizza) { super(customPizza); } public String make() { return customPizza.make() + addMozzarella(); } private String addMozzarella() { return " + Mozzarella"; } }
4 – Concrete component: This is the base of the pizza (the dough), the component that will generate different kinds of pizzas.
public class SimplePizza implements Pizza { @Override public String make() { return "Base"; } }
5 – Unit Tests: It’s test time! Now we will top the pizza in the way we want. We will instantiate the toppings first and then we use the base of the pizza – always passing the object in the constructor of each decorator class. In the end, we invoke the generic make method and the pizza is done!
Also, there is the Java API example with File creation.
public class DecoratorTest { @Test public void decoratorTest() { Pizza pizza = new TuscanyDecorator( new MozzarellaDecorator(new SimplePizza())); Assert.assertEquals("Base + Mozzarella + Tuscany", pizza.make()); } @Test public void decoratorJavaAPITest() throws IOException { File file = new File("./output.txt"); file.createNewFile(); OutputStream stream = new FileOutputStream(file); DataOutputStream dataStream = new DataOutputStream(stream); dataStream.writeChars("text"); dataStream.close(); stream.close(); file.delete(); } }
Summary of actions:
- Created the generic interface of the Component with the make method.
- Created the Component class implementing the generic interface.
- Created the Decorator abstract class controlling the decoration.
- Created the Decorator concrete classes extending the Decorator.
- Decorated the Component joining the classes in the constructor.
To practice the Decorator pattern you can create another Component class, for example, a Sandwich, and decorate your Sandwich as you wish. Be sure to use TDD (Test Driven Development).
The TuscanyDecorator.make method should contain addTuscany() instead of addDressing().
It’s changed! Thanks for the heads up 🙂
TuscanyDecorator class should have the constructor with the same name. It’s on the page, the sourcecode is correct
Hi Pavel, thanks for the comment. I just changed the constructor name to TuscanyDecorator. Keep the code on!
Design patterns in Saga, especially orchestrator are important in micro-services mesh. They help in keeping the code flexible enough to change. The article really completes the Saga and design pattern by a unit test. Good one