Decorator mode

Posted by poseidix on Wed, 19 Jan 2022 09:22:02 +0100

Decorator mode

1. Basic knowledge

Definition:

Function is added to the object without changing the original object

Provides a more flexible alternative than inheritance (extending the original object function)

Type: structural type

Through the construction method, the subclass is passed in, the subclass is wrapped, the base subclass object is received with the parent class in the decorator class, and the parent class object is returned, which can achieve unlimited packaging

Applicable scenario

Extend the functionality of a class or add additional responsibilities to a class

Dynamically add functions to an object, and these functions can be dynamically revoked

advantage

Inheritance is a powerful supplement, which is more flexible than inheritance, and extends the function of an object without changing the original object

Different effects can be achieved by using different decorative classes and the arrangement and combination of these decorative classes

Comply with the opening and closing principle

shortcoming

There will be more code, more classes and more program complexity

In dynamic decoration, multi-layer decoration will be more complex

Related design patterns

Decorator mode and agent mode

Decorator mode and adapter mode


2. Actual combat

Let's take milk tea as an example. A single cup of milk tea costs 8 yuan. One coconut is 2 yuan and one pudding is 3 yuan. I want to add two coconut and one pudding.

UML is shown in the figure below:

/**
 * Milk tea decorator class, an abstract class that combines milk tea in a class
 */
public abstract class AbstractDecorator extends ATeaMilk {
    //Inherit an abstract class and assemble the abstract object
    private ATeaMilk aTeaMilk;

    public AbstractDecorator(ATeaMilk aTeaMilk) {
        this.aTeaMilk = aTeaMilk;
    }

    protected abstract void doSomething();

    @Override
    protected String getDesc() {
        return this.aTeaMilk.getDesc();
    }

    @Override
    protected int cost() {
        return this.aTeaMilk.cost();
    }
}
/**
 * Milk tea base
 * @Author LYS
 * @Date 2022/1/18 16:38
 * @Version 1.0
 */
public abstract class ATeaMilk {
    protected abstract String getDesc();
    protected abstract int cost();
}
/**
 * Coconut decoration, 1 share, 2 yuan
 *
 * @Author LYS
 * @Date 2022/1/18 16:41
 * @Version 1.0
 */
public class CoconutDecorator extends AbstractDecorator {
    public CoconutDecorator(ATeaMilk aTeaMilk) {
        super(aTeaMilk);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDesc() {
        return super.getDesc() + " Add a coconut and 2 pieces";
    }

    //A coconut is 2 yuan
    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}
/**
 * Pudding 3 yuan a pudding
 * @Author LYS
 * @Date 2022/1/18 16:44
 * @Version 1.0
 */
public class PuddingDecorator extends AbstractDecorator{
    public PuddingDecorator(ATeaMilk aTeaMilk) {
        super(aTeaMilk);
    }

    @Override
    protected void doSomething() {

    }
    @Override
    protected String getDesc() {
        return super.getDesc() + " Add a pudding for 3 yuan";
    }

    //A pudding costs 3 yuan
    @Override
    protected int cost() {
        return super.cost() + 3;
    }
}
/**
 * @Author LYS
 * @Date 2022/1/18 16:40
 * @Version 1.0
 */
public class TeaMike extends ATeaMilk {
    @Override
    protected String getDesc() {
        return "Milk tea 8 pieces";
    }

    //Milk tea defaults to 8 yuan a cup
    @Override
    protected int cost() {
        return 8;
    }
}
public class Test {
    public static void main(String[] args) {
        //ATeaMilk is an abstract class of all classes
        ATeaMilk aTeaMilk;
        //TeaMike is the implementation class of ATeaMilk. It defines a cup of ordinary milk tea. The definition method cost is 8
        aTeaMilk = new TeaMike();
        //CoconutDecorator(); The implementation class definition method cost passed into the ATeaMilk base class is super cost()+2; That is, 8 + 2 returns the ATeaMilk object
        aTeaMilk = new CoconutDecorator(aTeaMilk);
        aTeaMilk = new CoconutDecorator(aTeaMilk);
        //PuddingDecorator(); The implementation class definition method cost passed into the ATeaMilk base class is super cost()+3; That is, 8 + 3 returns the ATeaMilk object
        aTeaMilk = new PuddingDecorator(aTeaMilk);
        //The cost() method is called here. It should be that the current aTeaMilk has been wrapped in several layers. This call is called continuously in and out of the stack
        System.out.println(aTeaMilk.getDesc() + " selling price:" + aTeaMilk.cost());
    }
}


3. Source code

java io

There are many ways to add cache classes

BufferedReader class inherits reader. Combined reader is a decorator class, which can inherit subclasses of reader indefinitely, similar to milk tea class in practice

public class BufferedReader extends Reader {

    private Reader in;
    
    public BufferedReader(Reader in, int sz) {
        super(in);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.in = in;
        cb = new char[sz];
        nextChar = nChars = 0;
    }

FilterInputStream inherits InputStream, and InputStream in is assembled in the class

class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;

TransactionAwareCacheDecorator in spring handles Cache and synchronization transactions. This class is the decorator class of Cache

public class TransactionAwareCacheDecorator implements Cache {

   private final Cache targetCache;
   public TransactionAwareCacheDecorator(Cache targetCache) {
       Assert.notNull(targetCache, "Target Cache must not be null");
       this.targetCache = targetCache;
   }

wrapper ends in either decorator mode or adapter mode, depending on the inheritance implementation structure of the specific class

In mybatis