Design
Overview
In software systems, we sometimes use inheritance to extend the function of an object, however, because inheritance is a static trait introduced by a type that makes this extension inflexible, and as the number of subclasses increases (expanded functionality increases), the combination of seed classes (the combination of extended features) causes more subclasses to swell. How do i make the extension of object functionality dynamically implemented on demand? At the same time to avoid the "expansion of the increase in function" of the problem of subclass expansion? So that any "functional expansion changes" will result in the lowest impact? This is the decorator mode that this article is going to talk about.
Intent
Dynamically add some additional responsibilities to an object. Decorator mode is more flexible than generating subclasses in terms of adding functionality. [GOF "Design pattern"]
structure Diagram
Figure 1 Decorator Pattern structure diagram
Examples of Life
The decoration mode dynamically adds additional responsibilities to an object. Whether a picture has a frame or not, it can be hung on the wall, but it is usually framed, and the frame is actually hung on the wall. Before hanging on the wall, the painting can be covered with glass and put into the frame, when the painting, the glass and the picture frames form an object.
Figure 2 Decorative Pattern object diagram using a picture frame drawing as an example
Decoration Mode explanation
In software development, it's often a matter of dynamically adding functionality to an object rather than an entire class, or to the example of my usual record log (perhaps this is not particularly appropriate in decorator mode). Now we are required to develop the log components, in addition to supporting the database records DatabaseLog and text file records Textfilelog Two ways, we also need to add some additional functionality in different application environments, such as the need to log information on the error severity level, You need to record the priority level of log information, as well as features such as extended properties of log information. Here, if we do not consider design patterns, the solution to the problem is very simple, can be implemented through the inheritance mechanism, the log class structure is as follows:
Figure 3
The implementation code is as follows:
Public abstract class Log
{
public abstract void Write (string log);
}
public class Databaselog:log
{
public override void Write (string log)
{
file://... Log in to the database
}
}
public class Textfilelog:log
{
public override void Write (string log)
{
file://... Log in to a text file
}
}
The ability to record the error severity level of log information and record the priority of logging information, as long as subclasses can be databaselog and textfilelog on the basis of the original subclass, and the introduction of two new interfaces Ierror and I Priority, the class structure diagram is as follows:
Figure 4
The implementation code is as follows:
public interface Ierror
{
void SetError ();
}
public interface Ipriority
{
void SetPriority ();
}
public class Dberrorlog:databaselog, Ierror
{
public override void Write (string log)
{
Base. Write (log);
}
public void SetError ()
{
file://... Feature extensions that enable logging error severity levels
}
}
public class Dbprioritylog:databaselog, ipriority
{
public override void Write (string log)
{
Base. Write (log);
}
public void SetPriority ()
{
file://... Feature extensions, achieving record priority levels
}
}
public class Tferrorlog:textfilelog, Ierror
{
public override void Write (string log)
{
Base. Write (log);
}
public void SetError ()
{
file://... Feature extensions that enable logging error severity levels
}
}
public class Tfprioritylog:textfilelog, ipriority
{
public override void Write (string log)
{
Base. Write (log);
}
public void SetPriority ()
{
file://... Feature extensions, achieving record priority levels
}
}
Now you can see that if you need the appropriate functionality, you can use these subclasses directly. Here we use the inheritance of classes to solve the problem of extension of object function, this way can achieve our intended purpose. However, it has brought a series of problems. First of all, the previous analysis is only an extension of the functionality, if you need to record the severity of the error and need to record priority, the subclass requires multiple inheritance of the interface, which in some cases violates the single duty principle of the class, note the blue area in the following figure:
Figure 5
Implementation code:
public class Dbeplog:databaselog, Ierror, ipriority
{
public override void Write (string log)
{
SetError ();
SetPriority ();
Base. Write (log);
}
public void SetError ()
{
file://... Feature extensions that enable logging error severity levels
}
public void SetPriority ()
{
file://... Feature extensions, achieving record priority levels
}
}
public class Tfeplog:databaselog, Ierror, ipriority
{
public override void Write (string log)
{
SetError ();
SetPriority ();
Base. Write (log);
}
public void SetError ()
{
file://... Feature extensions that enable logging error severity levels
}
public void SetPriority ()
{
file://... Feature extensions, achieving record priority levels
}
Secondly, with the expansion of later functions, subclasses will quickly expand, you can see that the emergence of subclasses is actually DatabaseLog and Textfilelog two subclasses and the newly added interface of an arrangement of the relationship, so the class structure will become very complex and difficult to maintain, As the Li Jianzhong teacher said, "subclasses subclass, how many subclasses"; Finally, the extension of this approach is a static extension, and there is no real implementation of the expansion of dynamic Add, the client program can not choose the way to add extended functionality and timing.
Now it's time for the decorator mode to come out, and the solution is to embed the log object in another object to extend the functionality. First we want to define an abstract wrapper class Logwrapper, let it inherit from the log class, the structure diagram is as follows:
Figure 6
The implementation code is as follows:
Public abstract class Logwrapper:log
{
Private Log _log;
Public logwrapper (log log)
{
_log = log;
}
public override void Write (string log)
{
_log. Write (log);
}
}
Now, for each extended feature, add a subclass of the wrapper class, allowing them to implement specific extension features, the green areas in the following figure:
Figure 7
The implementation code is as follows:
public class Logerrorwrapper:logwrapper
{
Public Logerrorwrapper (Log _log)
: Base (_log) {}
public override void Write (string log)
{
SetError (); file://... Feature Extensions
Base. Write (log);
}
public void SetError ()
{
file://... Record error severity level implemented
}
}
public class Logprioritywrapper:logwrapper
{
Public Logprioritywrapper (Log _log): Base (_log) {}
public override void Write (string log)
{
SetPriority (); file://... Feature Extensions
Base. Write (log);
}
public void SetPriority ()
{
file://... Record priority level implemented
}
}
Here, the Logerrorwrapper class and the Logprioritywrapper class actually implement an extension of the functionality of the error severity level and priority level. Let's take a look at how the client program calls it:
public class Program
{
public static void Main (string[] args)
{
Log log = new DatabaseLog ();
Logwrapper lew1 = new Logerrorwrapper (log);
file://Extended Logging error severity level
Lew1. Write ("Log message");
Logprioritywrapper lpw1 = new Logprioritywrapper (log);
FILE://extends the record priority level
Lpw1. Write ("Log message");
Logwrapper lew2 = new Logerrorwrapper (log);
Logprioritywrapper lpw2 = new Logprioritywrapper (LEW2); file://, this is lew2.
FILE://also expands error severity and priority levels
Lpw2. Write ("Log message");
}
}
Note In the above program of the third section of decoration to really reflect the decorator mode of subtlety, here a total of two times: the first log object to the wrong severity of the decoration, into the Lew2 object, the second time to decorate the Lew2 object, so became the Lpw2 object, The Lpw2 object at this time extends both the error severity level and the priority level functionality. That is to say, what functions we need, we can continue to package. There may be people here who say that the constructor of the Logprioritywrapper class receives a log object, why is it possible to pass the Logerrorwrapper object here? The class structure diagram shows that the Logerrorwrapper class is actually a subclass of the log class.
Let's analyze what benefits this will bring. First of all, there has been a real dynamic increase in extension functionality, packaging only when a function is required, and secondly, if a new extension is added, just add a corresponding wrapper subclass (Note: This will not be avoided at any time) without the need for many subclass inheritance. There is no expansion of subclasses, and the decorator pattern conforms well to the principle of object-oriented design, which is "priority to use object combination rather than inheritance" and "open-close".
decorative patterns in. NET
1.. NET Decorator mode A typical application is about the stream, which has the following class structure:
Figure 8
As you can see, BufferedStream and CryptoStream are actually two wrapper classes, where the decorator pattern omits the abstract decoration role (decorator) and the sample code is as follows:
Class Program
{
public static void Main (string[] args)
{
MemoryStream Ms =new MemoryStream (new byte[] {100,456,864,222,567});
FILE://extends the buffering function
BufferedStream buff = new BufferedStream (MS);
FILE://extends buffering, encryption capabilities
CryptoStream crypto = new CryptoStream (buff);
}
}
By recompiling, you can see the code for the BufferedStream class, which lists only the parts, and it inherits from the Stream class:
public sealed class Bufferedstream:stream
{
Methods
Private BufferedStream ();
Public BufferedStream (Stream stream);
Public BufferedStream (Stream stream, int buffersize);
Fields
private int _buffersize;
Private Stream _s;
}
2. The Daab in the Enterprise Library has a Dbcommandwrapper wrapper class that implements the packaging of the IDbCommand class and provides the function of parameter handling. The structure diagram is as follows:
Figure 9
The schematic code is as follows:
Public abstract class Dbcommandwrapper:marshalbyrefobject, IDisposable
{}
public class Sqlcommandwrapper:dbcommandwrapper
{}
public class Oraclecommandwrapper:dbcommandwrapper
{}
Effect and realization Essentials
1. The component class acts as an abstract interface in decorator mode and should not implement specific behavior. And the decorator class should be transparent to the component class, in other words, the component class does not need to know the decorator class, and the decorator class extends the functionality of the component class from the outside.
2. The Decorator class behaves as an inheritance of is-a component on an interface, that is, the decorator class inherits the interface that the component class has. However, in the implementation, it is represented by the combination of has-a component, that is, the decorator class uses another component class. We can use one or more decorator objects to "decorate" a Component object, and the decorated object is still a Component object.
3. Decortor mode is not to solve the problem of "multi-subclass-derived multiple inheritance", the key point of decorator mode is to solve "the extension function of the subject class in multiple directions"--it is the meaning of "adornment".
4. The application of Decorator mode in practice can be very flexible. If there is only one concretecomponent class and no abstract component class, then the decorator class can be a subclass of Concretecomponent.
Figure 10
If there is only one Concretedecorator class, then there is no need to create a separate decorator class, but to combine the responsibilities of decorator and Concretedecorator into a single class.
Figure 11
5. The advantage of the decorator model is that it provides a more flexible extension than inheritance, and can create a combination of many different behaviors by using different concrete decorative classes and the arrangement of these decorative classes.
6. Because of the use of decorative mode, you can use more than a few destination classes for inheritance relationships. Using fewer classes, of course, makes the design easier to do. However, on the other hand, using decorative mode produces more objects than using inheritance relationships. More objects make it difficult to find errors, especially if they look alike.
Applicability
Decorate mode should be used in the following situations:
1. You need to extend the functionality of a class or add additional responsibilities to a class.
2. You need to dynamically add functionality to an object that can be undone dynamically.
3. There is a need to increase the number of functions generated by the permutations and combinations of some basic functions, thus making the inheritance relationship impractical.
Summary
The decorator model adopts object combination rather than inheritance to realize the ability to extend object function dynamically at runtime, and can extend multiple functions as needed to avoid the "flexibility difference" and "multi-subclass derivation problem" brought by using inheritance alone. At the same time, it conforms to the principle of object-oriented design, "prioritize the use of objects rather than inheritance" and "open-closed" principles.