current position:Home>Decorator mode of design mode

Decorator mode of design mode

2022-01-27 02:38:52 codeXT

Definition

Without changing the original object , Add function to original function , Expand the function ,
Dynamically add some extra functions to an object , Decorating patterns are better than generating subclasses ( Inherit ) Implementation is more flexible .
In decorator mode , In order to make the system more flexible and scalable , We usually define an abstract decoration class , And take the specific decoration class as its subclass

type

Structural type

UML chart

Decorator (Decorator) And specific components (ConcreteComponent) All inherited from components (Component), The method implementation of a specific component does not need to depend on other objects , And the decorator combined a component , In this way, it can decorate other decorators or specific components . Decoration , It is to put the decorator on the decorator , So as to dynamically expand the function of the decorator . Part of the decorator's method is his own , It belongs to its function , Then the method of decorator is invoked , Thus it also retains the function of the decorator . You can see , The specific components should be the lowest level of decoration , Because only the method implementation of specific components does not need to depend on other objects .
 Insert picture description here

role

Component( Abstract components ): It is the common parent of concrete components and abstract decoration classes , Declare the business methods implemented in specific components , Its introduction enables the client to deal with undecorated objects and decorated objects in a consistent way , Realize the transparent operation of the client .

ConcreteComponent( Specific components ): It is a subclass of the abstract component class , Used to define specific component objects , Implements methods declared in abstract components , Decorators can add extra responsibility to it ( Method ).

Decorator( Abstract decorator ): It is also a subclass of the abstract component class , Used to add responsibilities to specific components , But the specific responsibilities are implemented in its subclasses . It maintains a reference to the abstract component object , Through this reference, you can call the method of decorating the previous component object , And extend the method through its subclasses , In order to achieve the purpose of decoration . about Component( Abstract components ) Come on , No need to know Decorator( Abstract decorator ) The presence of the ,Component( Abstract components ) Just do your part .

ConcreteDecorator( Concrete decoration ): It is a subclass of the abstract decoration class , Responsible for adding new responsibilities to the component . Each specific decoration class defines some new behavior , It can call methods defined in the abstract decoration class , And new methods can be added to extend the behavior of objects .

Because the concrete component class and decoration class implement the same abstract component interface , Therefore, the decoration mode dynamically attaches more responsibilities to an object in a transparent way to customers , In other words , The client doesn't think objects are different before and after decoration . Decoration patterns can be used without creating more subclasses , Extend the function of an object .

The core of decoration pattern is the design of abstract decoration class .

Example :

version1: Add functionality through inheritance to a class

Pancakes ( Realize basic functions )

public class Battercake {
    

    protected String getDesc(){
    
        return " pancakes ";
    }
    protected int cost(){
    
        return 8;
    }

}

Pancakes with eggs ( Add eggs to the pancake class by inheriting )

public class BattercakeWithEgg extends Battercake {
    
    @Override
    public String getDesc() {
    
        return super.getDesc()+"  Add an egg ";
    }

    @Override
    public int cost() {
    
        return super.cost()+1;
    }

}

Pancakes with eggs and sausage ( Use inheritance to continue adding new features )

public class BattercakeWithEggSausage extends BattercakeWithEgg {
    

    @Override
    public String getDesc() {
    
        return super.getDesc()+ "  Add a sausage ";
    }

    @Override
    public int cost() {
    
        return super.cost()+2;
    }
}

Test class

public class Test {
    

    public static void main(String[] args) {
    
        Battercake battercake = new Battercake();
        System.out.println(battercake.getDesc()+"  Selling price :"+battercake.cost());

        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getDesc()+"  Selling price :"+battercakeWithEgg.cost());

        Battercake battercakeWithEggSausage = new BattercakeWithEggSausage();
        System.out.println(battercakeWithEggSausage.getDesc()+"  Selling price :"+battercakeWithEggSausage.cost());
    }
}

 Insert picture description here

version2: Through decorator mode

Abstract components ( Abstract pancake class )

public abstract class ABattercake {
    

    protected abstract String getDesc();
    protected abstract int cost();

}

Specific components ( Pancakes )

public class Battercake extends ABattercake {
    
    @Override
    protected String getDesc() {
    
        return " pancakes ";
    }

    @Override
    protected int cost() {
    
        return 8;
    }
}

Abstract decorator , Abstract decoration class combines pancake abstract classes through member properties , It also inherits the pancake abstract class , And new functions have been added here doSomething()

public abstract class AbstractDecorator extends ABattercake {
    

    private ABattercake aBattercake;

    public AbstractDecorator(ABattercake aBattercake) {
    
        this.aBattercake = aBattercake;
    }

    protected abstract void doSomething();

    @Override
    protected String getDesc() {
    
        return this.aBattercake.getDesc();
    }
    @Override
    protected int cost() {
    
        return this.aBattercake.cost();
    }
}

Egg decorator , Inherited the abstract decoration class , The egg decorator adds an egg to the parent class , At the same time, the price plus 1 Yuan

public class EggDecorator extends AbstractDecorator {
    

    public EggDecorator(ABattercake aBattercake) {
    
        super(aBattercake);
    }

    @Override
    protected void doSomething() {
    
        System.out.println(" Get ready , I'm going to add eggs ");
    }

    @Override
    protected String getDesc() {
    
        return super.getDesc()+"  Add an egg ";
    }

    @Override
    protected int cost() {
    
        return super.cost()+1;
    }


    protected void dance(){
    
        System.out.println(" Watch me dance , Add eggs ");
    }


}

Sausage decorator , Similar to the egg decorator , Inherited the abstract decoration class , Add a sausage to the parent class , At the same time, the price increases 2 Yuan

public class SausageDecorator extends AbstractDecorator {
    
    public SausageDecorator(ABattercake aBattercake) {
    
        super(aBattercake);
    }

    @Override
    protected void doSomething() {
    
        System.out.println(" Are you ready , I'm going to start adding sausages ");
    }

    @Override
    protected String getDesc() {
    
        return super.getDesc()+"  Add a sausage ";
    }

    @Override
    protected int cost() {
    
        return super.cost()+2;
    }
    protected void sing(){
    
        System.out.println("are you ok?  I'm going to start adding sausages , Baked gluten, my baked gluten , A mistake , Come again ");
    }
}

test Test

public class Test {
    
    public static void main(String[] args) {
    
        ABattercake aBattercake;
        aBattercake = new Battercake();
        System.out.println(aBattercake.getDesc()+"  Selling price :"+aBattercake.cost());

        aBattercake = new EggDecorator(aBattercake);
        System.out.println(aBattercake.getDesc()+"  Selling price :"+aBattercake.cost());

// System.out.println(aBattercake.doSomething());
// System.out.println(aBattercake.dance());

        aBattercake = new SausageDecorator(aBattercake);
        System.out.println(aBattercake.getDesc()+"  Selling price :"+aBattercake.cost());

        // Translucent decorative pattern 
        EggDecorator eggDecorator = new EggDecorator(aBattercake);
        eggDecorator.doSomething();
        eggDecorator.dance();

        SausageDecorator sausageDecorator = new SausageDecorator(aBattercake);
        sausageDecorator.doSomething();
        sausageDecorator.sing();

// AbstractDecorator abstractDecorator = new EggDecorator(aBattercake);
// abstractDecorator.doSomething();
// abstractDecorator.dance();

    }
}

Pancake object or that pancake object , But there will be decorative objects to add new things to him , And the decoration of various decorative objects has no sequential relationship , Are directly decorated with the original pancake objects . If multiple decorations have sequential dependency ( For example, before storing data , Do data filtering and encryption . If you encrypt the data first , Then it's not easy to filter data , First filter the data , Then encrypt the data ), Then pay attention to the decoration order . But it's best to design the decorative things as independent , There is no sequential dependency , Can be combined in any order .
 Insert picture description here

Transparent decoration mode and translucent decoration mode

1. Transparent decorative pattern

In the transparent decoration mode, the client is required to program completely against the abstract , The transparency of decoration pattern requires that client programs should not declare objects as specific component types or specific decoration types , Instead, it should all be declared as abstract component types . For clients , There is no difference between concrete component class and concrete decoration class object .( shortcoming , The unique function of decoration class cannot be called alone )

The above example is the transparent decoration mode , Decorated objects are created by abstracting inter group class types ABattercake Variable to reference , Then it is injected into other specific decorative object instances through combination , But in this case, you can only call the methods defined in the abstract inter group class ( The specific decoration class does some decoration for these defined methods )

ABattercake aBattercake;
aBattercake = new EggDecorator(aBattercake);

2. Translucent decorative pattern

Define the decorated object with specific decoration type , Concrete component types can still be defined using abstract component types , The unique method of decoration can be called separately .( shortcoming : Cannot decorate more than once )

The design of transparent decoration mode is difficult , And sometimes we need to call the new business method alone . In order to be able to call the new method , We have to define the object after decoration with specific decoration type , The specific component type can still be defined by the abstract component type , This decoration mode is called translucent decoration mode .

Translucent decoration mode can bring more flexibility to the system , The design is relatively simple , It's also very convenient to use ; But its biggest disadvantage is that it can't decorate the same object many times , And the client needs to treat the object before decoration and the object after decoration differently .

ABattercake aBattercake;
EggDecorator eggDecorator = new EggDecorator(aBattercake);
eggDecorator.doSomething();
eggDecorator.dance();

SausageDecorator sausageDecorator = new SausageDecorator(aBattercake);
sausageDecorator.doSomething();
sausageDecorator.sing();

 Insert picture description here

Decorator Pattern summary

advantage

  • By using different decoration classes and the arrangement and combination of these classes , Different effects can be achieved .
  • The purpose of decorator pattern and inheritance relationship is to expand the function of the original object , But the decorator pattern can provide more flexibility than inheritance , It will not lead to a sharp increase in the number of classes
  • Class should be open to extension , Turn off for changes ( Comply with opening and closing principle ): Specific component classes and specific decoration classes can be changed independently , Users can add new specific component classes and specific decoration classes as needed , The original class library code does not need to be changed .
  • The responsibilities of specific component classes are clearer and simpler , Effectively distinguish the core responsibilities of classes from decorative functions , It won't produce many redundant functions , Simplify the original class , Only when a function is needed , Only the decorator can add functions . And this can also remove the repeated decorative logic in related classes

shortcoming

  • More code , More classes , Increase the complexity of the program .
  • Dynamic decoration 、 Multi layer decoration will make the system more complex .
  • Being more flexible also means being more error prone . Decoration mode provides a more flexible solution than inheritance , But it also means that it's more error prone than inheritance , Troubleshooting is also more difficult , For objects decorated many times , Finding errors during debugging may need to be checked step by step , More complicated
  • There will be a lot of small objects when using decoration mode for system design ( Large objects include small objects , Inject small objects into large objects in the form of composition ), A large number of small objects are bound to occupy more system resources , Affect the performance of a program on a certain program .

Applicable scenario

  • Extend the functionality of a class or add additional responsibilities to a class
  • Without affecting other objects , Dynamic 、 Add responsibilities to individual objects transparently
  • You need to dynamically add functionality to an object , These functions can also be revoked dynamically
  • When the system cannot be extended by inheritance or inheritance is not conducive to system expansion and maintenance, decoration mode can be used . There are two main types of cases in which inheritance cannot be used : The first is that there are a lot of independent extensions in the system , To support each extension or combination of extensions, there will be a large number of subclasses , It makes the number of subclasses increase explosively ; The second is because the class is defined as not inheritable ( Such as Java In language final class ).

Comparison of related design patterns

  • Decorator mode and agent mode : Decorator mode focuses on the dynamic addition of object functions . The proxy pattern focuses on controlling access to objects , Hide the specific information of the object from its users .
  • Decorator mode and adapter mode : The decorator pattern and the decorated class should implement the same interface , Or the decoration class is a subclass of the decorated class . The adapter pattern and the class being adapted have different interfaces .

application :

Java I/O Decorator mode in

 Insert picture description here
It can be seen from the picture above that Java Through the input stream (InputStream) Of Read Method to read bytes from the source address , And then through the output stream (OutputStream) Of Write Method writes the stream to the destination address . There are three main sources of flow :
① Local files (File)
② Console input
③ adopt socket Network communication realized

We can take a look at the pictures of other bloggers java IO The decorator mode of ,
As you can see from the figure below Java Decorator class and decorator class in and the relationship between them , Only... Is listed here InputStream Relationship in :
 Insert picture description here
It can be seen from the above figure that as long as we inherit FilterInputStream The class of is the decorator class , Can be used to wrap other streams , Decorator class can also repackage decorator and class

The following code is the translucent decoration mode used

public class StreanDemo {
    

    public static void main(String[] args) throws IOException {
    

        DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\JAVAworkspace\\ProgramTest\\src\\StreamDemo.java")));
        while(in.available()!=0)
        {
    
            System.out.print((char)in.readByte());
        }
        in.close();
    }

}

In the above procedure, the convection is packed twice , First use BufferedInputStream take FileInputStream Packaged as a buffered stream, that is, to FileInputStream Add buffer function , Again DataInputStream Further packaging to facilitate data processing .

Custom decoration class
Empathy , You can inherit abstract decoration classes , To implement the specific decoration class defined by yourself
The following code : Change all uppercase letters in the input stream to lowercase letters

public class LowerCaseInputStream extends FilterInputStream {
    

    protected LowerCaseInputStream(InputStream in) {
    
        super(in);
    }

    @Override
    public int read() throws IOException {
    
        int c=super.read();

        return (c==-1?c:Character.toLowerCase(c));
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
    
        int result=super.read(b, off, len);
        for(int i=off;i<off+result;i++)
        {
    
            b[i]=(byte)Character.toUpperCase((char)b[i]);
        }
        return  result;
    }

    public static void main(String[] args) throws IOException {
    
        int c;
        try (InputStream in = new LowerCaseInputStream(new FileInputStream( File path ))) {
    
            try {
    
                while ((c = in.read()) >= 0) {
    
                    System.out.print((char) c);
                }
            } finally {
    
                in.close();
            }
        }
    }

}

Result display
 Insert picture description here

Application scenarios of several common streams :

Stream name Application scenarios
ByteArrayInputStream Access array , Take a buffer in memory as InputStream Use ,CPU Data is read from the cache faster than from the storage medium 10 More than times
StringBufferInputStream Put one String Object as .InputStream. Not recommended , There is a defect in converting characters
FileInputStream access files , Take a document as InputStream , Realize the operation of reading files
PipedInputStream Access pipes , It is mainly used in threads , A thread sends data through a piped output stream , Another thread reads data through the pipeline input stream , In this way, the communication between two threads can be realized
SequenceInputStream The multiple InputStream Merge into one InputStream . “ Sequence input stream ” Class allows an application to combine several input streams continuously
DataInputStream Special flow , Read various basic types of data , Such as byte、int、String The function of
ObjectInputStream Object flow , The function of reading objects
PushBackInputStream Push back the input stream , Some of the data read in can be rolled back into the buffer of the input stream
BufferedInputStream Buffer flow , Added buffer function

References:

  • https://coding.imooc.com/class/270.html
  • https://www.pdai.tech/md/dev-spec/pattern/12_decorator.html
  • https://whirlys.blog.csdn.net/article/details/82764333
  • https://blog.csdn.net/u013309870/article/details/75735676
  • 《 Big talk design patterns 》
  • https://blog.csdn.net/qq_37960603/article/details/104087989
  • https://blog.csdn.net/weixin_44045828/article/details/110490324

( Blogging is mainly a summary of your own learning , Most of the information comes from books and online materials , Sorting is not easy to , But there are inevitably shortcomings , If there is a mistake , Please comment and correct . At the same time, I would like to thank the bloggers and authors for their hard work in sorting out the resources .)

copyright notice
author[codeXT],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270238453899.html

Random recommended