Design Mode 4 - Decorator Decorator Decorator

Keywords: network

Decorator mode belongs to "single responsibility" mode.
In the design of software components, if the division of responsibilities is not clear, the result of using inheritance is often that subclasses expand rapidly with the change of requirements, and at the same time, they are full of repetitive code, which is bad smell of code.
Typical single responsibility models: Decorator and Bridge. These two models show a strong "taste of single responsibility model". In this paper, we discuss the Decorator model.

motivation

In some cases, we may "use inheritance excessively to extend the functions of objects", because inheritance introduces static characteristics for types, which makes this extension method inflexible; and with the increase of subclasses (the increase of extended functions), the combination of various subclasses (the combination of extended functions) will lead to the expansion of more subclasses.

How can the expansion of object functions be realized dynamically according to the need, while avoiding the problem of subclass expansion caused by the increase of extended functions? Thus, any change of function expansion will have the lowest impact.

Example

Consider designing an IO library, mainly some stream operations. You can first define a class Stream.

//Business operation
class Stream{
public:
    virtual  char Read(int number)=0;
    virtual void Seek(int postion)=0;
    virtual void Write (char data)=0;
    ~Stream() { }
};

Based on Stream, derivative classes such as file flow, network flow, memory flow can be defined, and member functions of the base class can be rewritten.

class FileStream:public Stream{
public:
    
    virtual  char Read(int number){
        //File read
    }
    virtual void Seek(int postion){
        //Location file
    }
    virtual void Write (char data){
        //File write
    }
    


};

class NetworkStream:public Stream{
public:
    virtual  char Read(int number){
        //Network reading


    }
    virtual void Seek(int postion){
        //Locating Network Flow

    }
    virtual void Write (char data){
        //Network Writing

    }



};

class MemoryStream:public Stream{
public:
    virtual  char Read(int number){
        //memory read


    }
    virtual void Seek(int postion){
        //Locate memory stream

    }
    virtual void Write (char data){
        //Memory write

    }

};

Now we want to extend the functions of file flow, network flow, such as adding additional encryption operations in the process of stream reading and writing. What should we do about this?
First of all, let's look at how to solve this problem by inheritance.

The first is to encrypt the file read and write. At this time, three classes will be added, FileStream, NetworkStream and MemoryStream, respectively.

class  CryptoFileStream:public FileStream{
public:    
    virtual  char Read(int number){
        //Encryption operation
        FileStream::Read(number);
        //Encryption operation
    }
    virtual void Seek(int postion){
        //Encryption operation
        FileStream::Seek(postion);        
        //Encryption operation

    }
    virtual void Write (char data){
        
        //Encryption operation
        FileStream::Write(data);
        //Encryption operation

    }   



};

class  CryptoNetworkStream: public NetworkStream{
public:
    virtual  char Read(int number){
        //Encryption operation
        NetworkStream::Read(number);
        //Encryption operation
    }
    virtual void Seek(int postion){
        //Encryption operation
        NetworkStream::Seek(postion);        
        //Encryption operation

    }
    virtual void Write (char data){
        
        //Encryption operation
        NetworkStream::Write(data);
        //Encryption operation

    }   
    


};

class  CryptoMemoryStream: public MemoryStream{
public:
    virtual  char Read(int number){
        //Encryption operation
        MemoryStream::Read(number);
        //Encryption operation
    }
    virtual void Seek(int postion){
        //Encryption operation
        MemoryStream::Seek(postion);        
        //Encryption operation

    }
    virtual void Write (char data){
        
        //Encryption operation
        MemoryStream::Write(data);
        //Encryption operation

    }     


};

As you can see, there are more classes and many duplicate codes. This design is not good. Consider using combination instead of inheritance.
Instead of inheriting, we add a member variable to the encrypted class in a combined mode. In this way, we can replace three derived classes with an encrypted class. The encrypted file type depends on the value of the member stream_and can encrypt FileStream, Network and Memory accordingly.

class  CryptoStream {
private:
    Stream*  stream_;  // =new FileStream() ,new NetWorkSteam, new MemoryStream,
public:    
    virtual  char Read(int number){
        //Encryption operation
        stream_->Read(number);
        //Encryption operation
    }
    virtual void Seek(int postion){
        //Perform encryption operation
        stream_->Seek(postion);        
        //Encryption operation

    }
    virtual void Write (char data){
        
        //Encryption operation
        stream_->Write(data);
        //Encryption operation

    }   



};

At this point, we need to consider that members of CryptoStream are also virtual functions, which is unreasonable, because it is not a base class, in order to maintain interface consistency, we need to let him inherit Stream.

class  CryptoStream:public Stream {
private:
    Stream*  stream_;  // =new FileStream() ,new NetWorkSteam, new MemoryStream,
public:
    CryptoStream(Stream* stream):stream_(stream){
        
    }    
    virtual  char Read(int number){
        //Encryption operation
        stream_->Read(number);
        //Encryption operation
    }
    virtual void Seek(int postion){
        //Encryption operation
        stream_->Seek(postion);        
        //Encryption operation

    }
    virtual void Write (char data){
        
        //Encryption operation
        stream_->Write(data);
        //Encryption operation

    }   



};

At this time, there is a very wonderful phenomenon, that is, CryptoStream inherits both the base class and a member variable is a pointer to the base class.

Further, you can move the Stream* stream_field up to define a Decorator

class Decorator:public Stream{

public:
    Decorator(Stream* stream):stream_(stream){

    }
protected:
    Stream*  stream_;

};


class  CryptoStream:public Decorator {

public:
    CryptoStream(Stream* stream):Decorator(stream){

    }    
    virtual  char Read(int number){
        //Encryption operation
        stream_->Read(number);
        //Encryption operation
    }
    virtual void Seek(int postion){
        //Encryption operation
        stream_->Seek(postion);        
        //Encryption operation

    }
    virtual void Write (char data){
        
        //Encryption operation
        stream_->Write(data);
        //Encryption operation
    }   

};

Decorator mode is more flexible than generating subclasses (inheritance) (eliminating duplicate code and reducing the number of subclasses) in terms of adding functionality to an object by adding additional responsibilities dynamically.

summary

By using combination instead of inheritance, Decorator mode achieves the ability to dynamically extend object functions at runtime, and can expand multiple functions according to need, avoiding the "poor flexibility" and "multi-subclass derivation" problems caused by inheritance.

Decorator class is a Component inheritance relation on the interface, that is, Decorator class inherits the interface of Component class. But in implementation, Decorator class uses another Component class. Inheritance is for interface specification, and composition is for calling specific implementation class.

Decorator mode is not a solution. The problem of multi-inheritance derived from multi-subclasses. The key point of application of Decorator mode is to solve the problem. The extended function of main classes in many directions.

Posted by jasonok6 on Fri, 20 Sep 2019 06:09:55 -0700