"Single duty" mode
In the design of software components, if the division of responsibility is unclear, the result of using inheritance is often changed with the demand, the subclass expands rapidly, and the repetition code is full, the key is to draw responsibility.
Typical mode
-Decorator
-Bridge
1. Motivation
In some cases we may " use inheritance to extend the functionality of an object ", because inheritance is the static trait introduced by the type, which makes this extension inflexible, and as the number of subclasses increases (the expansion function increases), the combinations (extensions) of each seed class Causes expansion of more subclasses .
So how can the "Expansion of object functionality" be implemented dynamically as needed? At the same time, avoid the problem of sub-class expansion caused by the increase of expansion function. This results in the lowest impact of any "functional expansion change".
2. Example
problem Description : Design a set of flow-related classes, first defining an abstract base class stream, and then inheriting various, such as Filestream,networkstream,memorystream. The convection is then extended to operations such as cryptographic operations, copy operations, and so on.
Business Operation class Stream{public:virtual char Read (int number) =0; virtual void Seek (int position) =0; virtual void Write (char data) =0; Virtual ~stream () {}};//principal class, File stream class Filestream:public stream{public:virtual char read (int number) {//Read file stream} virtual void Seek (int position) {//Locate file stream} virtual void write (char data) {//write file stream}};//network stream class NetworkStream:p ublic stream{public:virtual char read (int number) {//Read network stream} virtual void Seek (int positio N) {//location network stream} virtual void write (char data) {//write network stream}};//memory Stream class MemoryStream:p ublic stream{publi C:virtual Char read (int number) {//Read memory stream} virtual void Seek (int position) {//Locate memory stream} virtual void write (char data) {//write memory Stream}};//extended operation, encrypted file stream class Cryptofilestream:p ublic filestream{public:virtual Char Read (int number) {//Extra encryption operation...Filestream::read (number);//Read file stream} virtual void Seek (int position) {//extra cryptographic operation...Filestream::seek (position);//location file stream//additional encryption operations...} virtual void Write (Byte data) {//Extra encryption operation...Filestream::write (data);//write file stream//additional encryption operation...}};//extended operation, encrypted network stream class Cryptonetworkstream:public networkstream{public:virtual char Read (int number) {//Extra encryption operation...Networkstream::read (number);//Read network stream} virtual void Seek (int position) {//extra cryptographic operation...Networkstream::seek (position);//Location Network stream//additional cryptographic operations...} virtual void Write (Byte data) {//Extra encryption operation...Networkstream::write (data);//write network stream//additional encryption operation...}};//extended operation, encrypted memory stream class Cryptomemorystream:public memorystream{public:virtual char Read (int number) {//Extra encryption operation...Memorystream::read (number);//Read memory stream} virtual void Seek (int position) {//extra cryptographic operation...Memorystream::seek (position);//Locate the memory stream//additional encryption operation...} virtual void Write (Byte data) {//Extra encryption operation...Memorystream::write (data);//write memory stream//extra encryption operation...}};//extended operation, cache file stream class Bufferedfilestream:public filestream{//...};//extended operation, cache network Stream class Bufferednetworkstream:public networkstream{//...};//extended operation, cache memory Stream class Bufferedmemorystream:public memorystream{//...}//extended operation, encrypted buffered file stream class Cryptobufferedfilestream:p ublic filestream{public:virtual char Read (int number) {//Extra encryption Operation...Additional buffering operations...Filestream::read (number);//Read file stream} virtual void Seek (int position) {//extra cryptographic operation...Additional buffering operations...Filestream::seek (position);//location file stream//additional encryption operations...Additional buffering operations...} virtual void Write (Byte data) {//Extra encryption operation...Additional buffering operations...Filestream::write (data);//write file stream//additional encryption operation...Additional buffering operations...}};void Process () {//compile-time assembly cryptofilestream *FS1 = new Cryptofilestream ();//Encrypting file stream Bufferedfilestream *FS2 = new Bufferedfilestream ();//cache file stream Cryptobufferedfilestream *fs3 =new cryptobufferedfilestream ();//Encrypt cache file stream}
Analysis:
The above example is about: the operation of a variety of streams, starting with only three requirements (file stream FileStream, network flow NetworkStream, memory flow MemoryStream), and then these three classes are inherited from an abstract class (Stream); After that, a variety of requirements, encryption operations, caching operations, and so on are proposed. So there's a variety of expansions. can see its relationship.
What's the problem? You can see that the above code has a large number of code redundancy (for example: The encryption operation is the same, whether for the file stream or network flow, the encrypted file stream Cryptofilestream read operations and encrypted network stream operations are first encrypted and reread), The same code. Take a look at the code that makes a partial change to Cryptofilestream,cryptonetworkstream,cryptomemorystream ( change inheritance to group ) (read-only operation)
class CryptoFileStream :{ FileStream *stream;//更改的地方public: virtual char Read(int number){ //额外的加密操作... stream->Read(number);//更改的地方 }};class CryptoNetworkStream{ NetworkStream* stream;//更改的地方public: virtual char Read(int number){ //额外的加密操作... stream->Read(number);//更改的地方 }};class CryptoMemoryStream{ MemoryStream* stream;//更改的地方public: virtual char Read(int number){ //额外的加密操作... stream->Read(number);//更改的地方 }};
The above code is not much like, where the members of each class added (FileStream stream,networkstream stream,memorystream* Stream) can be further changed to stream* Stream on it. The code is changed as follows
class CryptoFileStream :{ Stream *stream;//new FileStream()public: virtual char Read(int number){ //额外的加密操作... stream->Read(number); }};class CryptoNetworkStream{ Stream* stream;//new NetworkStream()public: virtual char Read(int number){ //额外的加密操作... stream->Read(number); }};class CryptoMemoryStream{ Stream* stream;// new NetWorkStream()public: virtual char Read(int number){ //额外的加密操作... stream->Read(number); }};
compile-time, run-time is not the same as the principle of most design patterns, run-time let him change ( with polymorphism to support their changes ).
After doing this, you find that these three classes are not exactly the same, that only need a class on the line (wonderful!!! )。 This eliminates the duplication and optimizes the code as follows
class CryptoFileStream :{ Stream *stream;//...可以有各种各样的流到这里public: virtual char Read(int number){ //额外的加密操作... stream->Read(number); }};
The above code, but you have not found a problem, cryptofilestream in the read by what is virtual function, so must inherit the base class (is to improve the interface specification), so modify the following
class CryptoStream: public Stream { Stream* stream;//...各种流都可以public: CryptoStream(Stream* stm):stream(stm){ } virtual char Read(int number){ //额外的加密操作... stream->Read(number);//读文件流 } virtual void Seek(int position){ //额外的加密操作... stream::Seek(position);//定位文件流 //额外的加密操作... } virtual void Write(byte data){ //额外的加密操作... stream::Write(data);//写文件流 //额外的加密操作... }};
This cryptofilestream has both a base class field and a base class, and the same code optimization for buffer operations is the same as the appeal method.
When this is done, you can use
void Process(){ //运行时装配 FileStream* s1=new//文件流 CryptoStream* s2=new CryptoStream(s1);//加密文件流 BufferedStream* s3=new BufferedStream(s1);//缓冲文件流 BufferedStream* s4=new BufferedStream(s2);//缓冲加密文件流}
what does the run-time assembly mean? There is no cache file stream at compile time, what encrypted file stream, etc., there is no such class, the runtime can be assembled together to meet the requirements. This is the meaning of decoration, decoration is attached to other objects
The diagram is as follows
The above approach has been perfected. However, if a subclass of a class has the same field, it should be raised upwards . What did you mention?
Method One: Refers to the base class. But FileStream does not need this field. The reference to the base class is inappropriate.
So you need to design the intermediate class , see the third version below
//Business operationsClass stream{ Public:Virtual Char Read(intNumber) =0;Virtual voidSeek (intPosition) =0;Virtual voidWrite (Chardata) =0;Virtual~stream () {}};//Main classClass FileStream: Publicstream{ Public:Virtual Char Read(intNumber) {//Read file stream}Virtual voidSeek (intPosition) {//Location file stream}Virtual voidWrite (CharData) {//write file stream}};class NetworkStream: Publicstream{ Public:Virtual Char Read(intNumber) {//Read network stream}Virtual voidSeek (intPosition) {//Location Network flow}Virtual voidWrite (CharData) {//write network stream}};class MemoryStream: Publicstream{ Public:Virtual Char Read(intNumber) {//Read memory stream}Virtual voidSeek (intPosition) {//Location Memory stream}Virtual voidWrite (CharData) {//write memory stream}};//Extended operation, Intermediate classDecoratorstream: Publicstream{/protected: stream* Stream;//...Decoratorstream (Stream * stm): Stream (STM) {}};class CryptoStream: PublicDecoratorstream { Public:CryptoStream(stream* stm):Decoratorstream(STM) { }Virtual CharRead (intNumber) {//Extra encryption Operation ...Stream->read (number);//Read file stream}Virtual voidSeek (intPosition) {//Extra encryption Operation ...Stream::seek (position);//Location file stream //Extra encryption Operation ...}Virtual voidWrite (byteData) {//Extra encryption Operation ...Stream::write (data);//write file stream //Extra encryption Operation ...}};class BufferedStream: Publicdecoratorstream{stream* Stream;//... Public:BufferedStream(stream* stm):Decoratorstream(STM) { }//...};
Summarize
- In the above code optimization process, some classes never move, but because the pattern of the nature of expansion, is on whose basis to do again, this is the meaning of decoration, attached to another place in an operation.
- The reason why the code is bad is the bad use of inheritance , static characteristics caused by static, but the combination can be well implemented dynamic (combinatorial better than inheritance)
Of course, the object-oriented design principle has a "use combination instead of inheritance"
3. Pattern definition
Dynamically (combined) adds an additional responsibility to an object. In terms of increased functionality. Decorator mode is more flexible than generating subclasses (inheritance) (eliminating duplicate code & reducing the number of subclasses)
The structure diagram is as follows
4. Summary
- By adopting a combination rather than an inheritance approach, the decorator pattern implements the ability to dynamically extend objects at run time , and it is necessary to extend multiple functions. Avoids the "poor flexibility" of using inheritance, and multi-subclass derivative functions.
- The decorator class becomes an inheritance of Is-a Component on the interface, that is, the decorator class inherits all the interfaces of the Component class. But in the realization of the HAS-A component the combination of the relationship, that is, the decorator class and use another component class
- The purpose of Decortor mode is not to solve the problem of "multiple inheritance of multiple subclasses", and the main point of application of decorator mode is to solve "the extended function of principal class in multiple directions"-this is the meaning of decoration
C + + design mode < six >:decorator decoration mode