Software design mode learning decoration mode


Decorator pattern is a kind of technology used to replace inheritance. It dynamically adds responsibilities to objects by a way that does not need to define subclasses, and makes the relationship between objects replace the inheritance relationship between classes.


Model motivation

The decorator can add additional new behaviors to the object on the basis of not changing an object itself, such as a photo, can add a picture frame to her without changing the photo itself, so that it has moisture-proof function, and users can add different types of picture frames according to their needs. In the software development, similar to adding picture frame to photos can be seen everywhere, such as adding frame, scrolling and other new features to a graphical interface component. There are two ways to add behavior to a class or object:

  1. Inheritance mechanism

    By inheriting an existing class of a class, a subclass can have its own method as well as its parent method. But this way is static, and users can't control the way and time of increasing behavior.

  2. Association mechanism

    The object of one class is embedded in another new object. Another object decides whether to call the behavior of the embedded object and extend its behavior. We call this new object (that is, another object) a decorator.


Schema definition

Dynamically add some extra responsibilities to an object. In terms of adding object functions, the decorator pattern is more flexible than generating subclass object implementation. Its alias can also be called a Wrapper.


Pattern structure

  1. Component (abstract component)

    Abstract components define the interfaces of objects, which can dynamically add responsibilities (Methods) to these objects. Abstract component is the common parent class of concrete component and abstract decoration class, which declares the business methods implemented in concrete component.

  2. ConcreteComponent (specific component)

    Concrete component defines concrete component object and realizes the method declared in abstract component.

  3. Decorator (abstract decoration class)

    Abstract decoration class is a subclass of abstract component class, which is used to add responsibility to concrete component, but concrete responsibility is implemented in its subclass. It maintains a reference to an abstract component object, through which a method of the component object before decoration is called, and the method is extended by a subclass.

  4. ConcreteDecorator (specific decoration class)

    The concrete decoration class is a subclass of the abstract decoration class, which is responsible for adding new responsibilities to the component. Each concrete decoration class defines some new behaviors. It can call the methods defined in the abstract decoration class, and add new methods to expand the behavior of the object.


Multiple encryption system of instances

A system provides a data encryption function, which can encrypt strings. The system provides simple shift encryption algorithm, slightly complex reverse output encryption and higher-level modulo encryption. Users first use the simplest shift encryption algorithm to encrypt the string. If it is not enough, users can encrypt the encrypted result twice or even three times.

  1. Abstract component class Cipher (Abstract encryption class)
    public interface Cipher {
    	
        //Method is the string to be encrypted, and the return value is the ciphertext after encryption
        public String encrypt(String plantTetx);
    }
    
  2. Concrete component class SimpleCipher (simple encryption class)
    public class SimpleCipher implements Cipher {
    	
        /*
         *	Implementation of encryption method by Caesar encryption
         */
        @Override
        public String encrypt(String plantTetx) {
            String str = "";
            for (int i = 0; i < plantTetx.length(); i++) {
                char c = plantTetx.charAt(i);
                if (c >= 'a' && c <= 'z') {
                    c += 6;
                    if (c > 'z') c -= 26;
                    if (c < 'a') c += 26;
                }
                if (c >= 'A' && c <= 'Z') {
                    c += 6;
                    if(c > 'Z') c -= 26;
                    if(c < 'A') c += 26;
                }
                str += c;
            }
            return str;
        }
    }
    
  3. Abstract decoration class CipherDecorator (encryption decoration class)
    public class CipherDecorator implements Cipher {
    
        private Cipher cipher;
    
        public CipherDecorator(Cipher cipher) {
            this.cipher = cipher;
        }
    	
        @Override
        public String encrypt(String plantTetx) {
            // Call cipher object's encrypt() method
            return cipher.encrypt(plantTetx);
        }
    }
    
  4. Specific decoration class ComplexCipher (complex encryption class)
    public class ComplexCipher extends CipherDecorator {
    
        public ComplexCipher(Cipher cipher) {
            super(cipher);
        }
    	
        // The encrypt() method of the parent class was called
        // And through the new reserve() method to further process the encrypted string
        public String encrypt(String plainText) {
            String result = super.encrypt(plainText);
            result = reverse(result);
            return result;
        }
    
        public String reverse(String text) {
            String str = "";
            for (int i = text.length(); i > 0; i--) {
                str += text.substring(i - 1, i);
            }
            return str;
        }
    }
    
  5. Specific decoration class AdvancedCipher (Advanced Encryption class)
    public class AdvancedCipher extends CipherDecorator {
    
        public AdvancedCipher(Cipher cipher) {
            super(cipher);
        }
    	
        // The encrypt() method of the parent class was called
        // The encrypted string is further processed by the new mod() method
        @Override
        public String encrypt(String plantTetx) {
            String result = super.encrypt(plantTetx);
            result = mod(result);
            return result;
        }
    
        public String mod(String text) {
            String str = "";
            for (int i = 0; i < text.length(); i++) {
                String c = String.valueOf(text.charAt(i) % 6);
                str += c;
            }
            return str;
        }
    }
    
  6. Test code Client
    public class Client {
    
        public static void main(String[] args) {
    
            String password = "sunnyLiu";   //Plaintext
            String cpassword;   //ciphertext
    
            Cipher sc = new SimpleCipher();
            cpassword = sc.encrypt(password);
    
            System.out.println(cpassword);
            System.out.println("---------------------");
    
            Cipher cc = new ComplexCipher(sc);
            cpassword = cc.encrypt(password);
            System.out.println(cpassword);
            System.out.println("---------------------");
    		
            //You can continue to decorate cc objects after decoration
            //In this way, the string can be further processed to obtain more complex encryption results
            Cipher ac = new AdvancedCipher(cc);
            cpassword = ac.encrypt(password);
            System.out.println(cpassword);
            System.out.println("---------------------");
        }
    }
    
  7. Operation results


Advantages and disadvantages of the model

The advantages of decoration mode are as follows:

  1. In terms of extending object functions, the decorator pattern is more flexible than the inheritance pattern
  2. The object function can be extended in a dynamic way, and different decorators can be selected by configuration files at runtime, so as to achieve different behaviors
  3. Multiple specific decoration classes can be used to decorate the same object to get more powerful objects
  4. Users can add new specific component classes and specific decoration classes according to their needs. The original code does not need to be changed and conforms to the opening and closing principle

The disadvantages of decoration mode are as follows:

  1. Using decorator pattern to design the system will produce many small objects and decoration classes, which increases the complexity of the system
  2. Programs are more error prone, and troubleshooting is more difficult

Applicable scenario of mode

Decoration mode can be considered in the following cases

  1. Add responsibilities to individual objects transparently and dynamically without affecting other objects
  2. When inheritance cannot be used to expand the system or it is not conducive to system expansion and maintenance. There are two main types of cases where inheritance cannot be adopted: the first is that there are a large number of independent extensions in the system, which will generate a large number of subclasses to support each combination; the second is because the class cannot inherit (final class)

Simplification of decoration mode

In most cases, the implementation of the decoration mode is simpler than the standard structure diagram, and the decoration mode can be simplified. The following problems should be paid attention to in the process of simplification:

  1. The interface of a decorated class must be the same as that of the decorated class. For clients, objects before and after decoration can be treated equally

  2. Don't put too much logic and state in concrete component classes, which can be extended by decoration classes

  3. If there is only one concrete component class but no abstract component class, the abstract component class can be regarded as a subclass of concrete component class

    If there is only one concrete decoration class, there is no need to design a separate abstract decoration class. You can combine the responsibilities of the abstract decoration class and the concrete decoration class into one class




Transparent decoration mode and translucent decoration mode

In the transparent decoration mode, the client is required to be completely aimed at Abstract Programming. The transparency of decoration mode requires that the client program should not declare specific component type and specific decoration type, but all of them should be declared as abstract component type. For example, the above encryption system is transparent decoration mode

Cipher sc = new SimpleCipher();
Cipher cc = new ComplexCipher(sc);
Cipher ac = new AdvancedCipher(cc);

The purpose of decoration mode is to enhance the function of the original class without changing the interface. Users often need to create new methods when enhancing functions. For example, if they want to directly use the reverse() method in the complex encryption algorithm, they need to use the translucent decoration mode

SimpleCipher sc = new SimpleCipher();
ComplexCipher cc = new ComplexCipher(sc);
AdvancedCipher ac = new AdvancedCipher(cc);

The application of Java IO to decoration mode

I don't introduce IO stream too much here. Take InputStream and OutputStream for example. They only provide the simplest stream processing method, which can only read and write characters, without buffer processing and can't process files.

InputStream is the parent class of all byte input streams, in which the declared read and operation data methods will be inherited by all byte input streams. There is a FilterInputStream class in the subclass, which contains some subclasses, such as BufferedInputStream used to add buffer function to an input stream, DataInputStream used to read the original type of data, etc.

The hierarchy of InputStream corresponds to the structure of decoration pattern. InputStream is an abstract class, which corresponds to the abstract component class in decoration pattern. While FilterInputStream, ByteArrayInputStream and so on all directly inherit InputStream class, they implement the read() method defined in InputStream. The FilterInputStream class is also a subclass of InputStream, which corresponds to the role of the abstract decoration class.

public class FilterInputStream extends InputStream {
	
    protected volatile InputStream in;
	
    // Constructor needs to pass a reference to an InputStream object
    // in object can be any reference inherited from InputStream type
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    ...
}

BufferInputStream is a subclass of FilterInputStream, which is equivalent to the specific decoration class in the decoration mode, and makes different decorations for the incoming input stream. The BufferInputStream class provides a caching mechanism. It uses an array as the buffer for data reading, and covers the read() method of the parent class. Before calling the input stream to read data, it will check whether the cache is full. It realizes the purpose of dynamically adding new functions to the input stream object. The new function here is buffer control.

FileInputStream inFS = new FileInputStream("temp/fileSrc.txt");
BufferedInputStream inBS = new BufferedInputStream(inFS);
//Define a byte array to hold buffered data
byte[] data = new byte[1024];
inBS.read(data);

In Java IO, not only InputStream uses decoration mode, but also OutputStream, Reader, Writer, etc.

Tags: Java Programming

Posted on Mon, 04 May 2020 02:51:01 -0700 by jgh84