Decorator mode

What is the decorator pattern?

We all know decoration, New Year’s day, Christmas, we all need decoration, render the festive atmosphere. The so-called decoration is to add things on the original basis.

The Decorator Pattern allows you to add new functionality to an existing object without changing its structure. This type of design pattern is a structural pattern that wraps around existing classes.

This pattern creates a decorator class that wraps the original class and provides additional functionality while preserving the integrity of the class method signature. We use the following example to demonstrate the use of the decorator pattern. Among them, we will put the Christmas room on the original basis decorated with balloons.

Let’s look at the UML diagram: in the beginning there were houses, houses had bungalows, buildings, villas, and we would clean the rooms.

/** * house abstract class */
public interface IHouse {
    void clean(a);
}

/** ** ** /
public class Bungalow implements IHouse{
    @Override
    public void clean(a) {
        // Clean the room}}/** * building */
public class HighRiseBuilding implements IHouse{
    @Override
    public void clean(a) {
        // Clean the room}}/** ** ** */
public class Villa implements IHouse{
    @Override
    public void clean(a) {
        // Clean the room}}Copy the code

At this time, encounter holidays, such as: Christmas, New Year’s day, we will decorate the room, decorate the room with balloons and so on. We can use the decorator design pattern. At this time, the main body of decoration is still the house. The decoration is like a shell covering the outside of the house.

/** * house abstract class */
public interface IHouse {
    void clean(a);
}

/** ** ** /
public class Bungalow implements IHouse{
    @Override
    public void clean(a) {
        // Clean the room
        System.out.println("I'm bungalow: Clean the house."); }}/** * building */
public class HighRiseBuilding implements IHouse{
    @Override
    public void clean(a) {
        // Clean the room
        System.out.println("I am the building: Clean the house"); }}/** ** ** */
public class Villa implements IHouse{
    @Override
    public void clean(a) {
        // Clean the room
        System.out.println("I am villa: Clean the house"); }}/** ** room decorator */
public abstract class DecoratorHouse implements IHouse {
    // The house is decorated
    protected IHouse decoratedHouse;

    public DecoratorHouse(IHouse decoratedHouse) {
        this.decoratedHouse = decoratedHouse; }}public class BalloonDecoratorHouse extends DecoratorHouse {


    public BalloonDecoratorHouse(IHouse decoratedHouse) {
        super(decoratedHouse);
    }

    @Override
    public void clean(a) {
        // Clean the room
        super.decoratedHouse.clean();
        ballonDecorate();
    }

    public void ballonDecorate(a) {
        // Decorate the room with balloons
        System.out.println("Decorate the room with balloons. It's beautiful."); }}public class DecoratorPatternTest {
    public static void main(String[] args) {
        IHouse house = new Villa();
        house.clean();

        System.out.println("------- It's Christmas, decorate your room -------");
        IHouse decoratorHouse = new BalloonDecoratorHouse(newVilla()); decoratorHouse.clean(); }}Copy the code

Using the decorator mode, we extended the house without changing it. During the holidays, we can use the decoration class to decorate the house. If the holiday is over, we continue to use the original house.

Next, let’s summarize some of the above cases. First, we have a Component with some custom functionality in it. Normally, the Component is abstract (interface or abstract class). Then, the abstract class has a concrete implementation called ConcreteComponent. In addition to implementing Component methods, you can also customize methods. Third, we now want to wrap the concrete implementation ConcreteComponent by defining a wrapper class, DecoratorComponent. Usually this wrapper class is abstract. The wrapper class also implements the Component interface and then introduces a specific Component member variable. Why introduce member variables? This also makes sense, our target is the concrete class that wraps. Fourth, define a concrete DecoratorComponent to which you can add additional methods. Like adding some logic after the previous latter. The specific relationship diagram is as follows:

The code implementation is as follows:

/** * abstract function */
public interface Component {
    void operate(a);
}

/** ** ** ** /
public class ConcreteComponent implements Component{
    @Override
    public void operate(a) {
        // Implement concrete logic
        System.out.println("Logic of concrete implementation"); }}/** * decorates the Component object's class */
public abstract class DeceratorComponent implements Component{
    // Specify the object to decorate
    protected Component deceratedComponent;

    public DeceratorComponent(Component deceratedComponent) {
        this.deceratedComponent = deceratedComponent; }}/** * Specific decoration class */
public class ConcreteDeceratorComponent extends DeceratorComponent {

    public ConcreteDeceratorComponent(Component deceratedComponent) {
        super(deceratedComponent);
    }

    @Override
    public void operate(a) {
        before();
        super.deceratedComponent.operate();
        after();
    }

    public void before(a){
        System.out.println("Added logic before original logic.");
    }

    public void after(a){
        System.out.println("Add logic after original logic."); }}public class DeceratorTest {
    public static void main(String[] args) {
        Component concreteCom = new ConcreteComponent();
        concreteCom.operate();

        System.out.println("= = = = = = = = = = = =");
        DeceratorComponent decerator = new ConcreteDeceratorComponent(newConcreteComponent()); decerator.operate(); }}Copy the code

Running results:

Implementation logic

============ adds logic before the original logic the logic of the concrete implementation adds logic after the original logic

Flexible application of design patterns. Suppose there is only one concrete class. We don’t need to define an abstract Component. How do you implement the decorator pattern? Let the decorator inherit directly from the original class:

Two. The characteristics of the decorator mode:

  1. Decorative objects have the same interface as real objects. This allows the client object to interact with the decorator object in the same way as the real object.
  2. The decorator object contains a reference to the real object.
  3. The decorator object accepts all requests from the client. It forwards these requests to real objects.
  4. Decorator objects can add additional functionality before or after forwarding these requests. This ensures that additional functionality can be added externally at run time without changing the structure of a given object. In object-oriented design, it is common to extend the functionality of a given class through inheritance.

Iii. Use scenarios of decorator mode:

Consider using the decorator pattern for the following situations

  1. You need to extend the functionality of a class or add additional responsibilities to a class.
  2. You need to add functions to an object dynamically, and those functions can be undone dynamically.
  3. The need to add a very large number of functions from permutations and combinations of basic functions makes inheritance unrealistic.
  4. When the method of subclass generation cannot be used to extend. In one case, there may be a large number of independent extensions, and the number of subclasses to support each combination will explode. Another case might be because the class definition is hidden, or the class definition cannot be used to generate subclasses.

Four. Advantages of decorator mode:

  1. The purpose of both the Decorator pattern and inheritance relationships is to extend the functionality of objects, but decorators can provide more flexibility than inheritance.
  2. By using different concrete decoration classes and permutations of these decoration classes, designers can create many combinations of different behaviors.

Disadvantages of the decorator pattern

  1. This more flexible nature than inheritance also means more complexity.
  2. Decorator patterns lead to many small classes in your design, which can complicate your program if overused.
  3. Decorator is designed for abstract Component types. However, if you are programming for specific components, you should rethink your application architecture and the appropriateness of decorators. You could also change the Component interface to add new exposed behaviors and implement a “translucent” decorator pattern. Make the best choice in the actual project.

Vi. Consider: What design pattern principles do decorator patterns use?

  1. The most obvious manifestation of this is the open closed principle – for extension development, closed for modification. When new requirements arrive, extend without changing the original functionality.

  2. Dependency inversion principle – dependence on the abstract, not the concrete. Easy to expand, and then a new house, do not modify the house decoration class.

  3. Single responsibility principle: a class is responsible for one thing