Decorator Mode
Decorator mode, also known as Wrapper mode [GOF95]. The decoration mode extends the object functions in a transparent way to the client. It is an alternative to the inheritance relationship.
Introduction
Sun Wukong has a change, and each of his changes brings him an additional skill. When he becomes a fish, he can swim in the water; when he becomes a sparrow, he can fly in the sky. However, no matter how Wukong changes, in the eyes of Erlang, he will always be the hacker.
The decoration mode dynamically attaches more responsibilities to an object in a transparent manner to the customer. In other words, the client does not think that objects are different before and after decoration. The decoration mode can extend the functions of objects without creating more child classes.
Ii. Structure of the decoration Mode
The decoration mode uses an instance of a subclass of the originally decorated class to delegate the call of the client to the decorated class. The key to the decoration mode is that the extension is completely transparent.
In the example of Sun monkey, the old sun becomes a child of the old sun. The interaction between the old Sun and the outside world should be "delegated", handed over to the old sun, and the old sun will take action.
Shows the decoration mode class diagram:
In the decoration mode, the roles are:
Abstract Component role: provides an abstract interface to standardize the objects that are prepared to receive additional responsibilities.
Concrete Component role: defines a class that will receive additional responsibilities.
Decorator: holds an instance of a Component object and defines an interface consistent with the abstract Component Interface.
Concrete Decorator: attaches "additional responsibilities to the component object.
3. decoration mode DEMO code
The following DEMO code implements the decoration Mode
// Decorator pattern -- Structural example using System;// "Component"abstract class Component{ // Methods abstract public void Operation();}// "ConcreteComponent"class ConcreteComponent : Component{ // Methods override public void Operation() { Console.WriteLine("ConcreteComponent.Operation()"); }}// "Decorator"abstract class Decorator : Component{ // Fields protected Component component; // Methods public void SetComponent( Component component ) { this.component = component; } override public void Operation() { if( component != null ) component.Operation(); }}// "ConcreteDecoratorA"class ConcreteDecoratorA : Decorator{ // Fields private string addedState; // Methods override public void Operation() { base.Operation(); addedState = "new state"; Console.WriteLine("ConcreteDecoratorA.Operation()"); }}// "ConcreteDecoratorB"class ConcreteDecoratorB : Decorator{ // Methods override public void Operation() { base.Operation(); AddedBehavior(); Console.WriteLine("ConcreteDecoratorB.Operation()"); } void AddedBehavior() { }}/**//// <summary>/// Client test/// </summary>public class Client{ public static void Main( string[] args ) { // Create ConcreteComponent and two Decorators ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB(); // Link decorators d1.SetComponent( c ); d2.SetComponent( d1 ); d2.Operation(); }}
The above code is implemented through the SetComponent method during decoration execution. In actual applications, it is also implemented through constructors. A typical creation process may be as follows:
new Decorator1( new Decorator2( new Decorator3( new ConcreteComponent() ) ) )
The decoration mode is often called the package mode because each specific decoration class wraps the next specific decoration class or specific component class.
4. Where should I use the decoration mode?
The decoration mode should be used in the following circumstances:
You need to expand the functions of a class or add additional responsibilities to a class.
You need to dynamically add functions to an object. These functions can be dynamically revoked.
A large number of functions are generated by the arrangement and combination of some basic functions to make the inheritance relationship unrealistic.
5. Example of practical application of decoration Mode
This example demonstrates how to add "loose" decoration to library books and video tapes in decoration mode.
// Decorator pattern -- Real World example using System;using System.Collections;// "Component"abstract class LibraryItem{ // Fields private int numCopies; // Properties public int NumCopies { get{ return numCopies; } set{ numCopies = value; } } // Methods public abstract void Display();}// "ConcreteComponent"class Book : LibraryItem{ // Fields private string author; private string title; // Constructors public Book(string author,string title,int numCopies) { this.author = author; this.title = title; this.NumCopies = numCopies; } // Methods public override void Display() { Console.WriteLine( " Book ------ " ); Console.WriteLine( " Author: {0}", author ); Console.WriteLine( " Title: {0}", title ); Console.WriteLine( " # Copies: {0}", NumCopies ); }}// "ConcreteComponent"class Video : LibraryItem{ // Fields private string director; private string title; private int playTime; // Constructor public Video( string director, string title, int numCopies, int playTime ) { this.director = director; this.title = title; this.NumCopies = numCopies; this.playTime = playTime; } // Methods public override void Display() { Console.WriteLine( " Video ----- " ); Console.WriteLine( " Director: {0}", director ); Console.WriteLine( " Title: {0}", title ); Console.WriteLine( " # Copies: {0}", NumCopies ); Console.WriteLine( " Playtime: {0}", playTime ); }}// "Decorator"abstract class Decorator : LibraryItem{ // Fields protected LibraryItem libraryItem; // Constructors public Decorator ( LibraryItem libraryItem ) { this.libraryItem = libraryItem; } // Methods public override void Display() { libraryItem.Display(); }}// "ConcreteDecorator"class Borrowable : Decorator{ // Fields protected ArrayList borrowers = new ArrayList(); // Constructors public Borrowable( LibraryItem libraryItem ) : base( libraryItem ) {} // Methods public void BorrowItem( string name ) { borrowers.Add( name ); libraryItem.NumCopies--; } public void ReturnItem( string name ) { borrowers.Remove( name ); libraryItem.NumCopies++; } public override void Display() { base.Display(); foreach( string borrower in borrowers ) Console.WriteLine( " borrower: {0}", borrower ); }} /**//// <summary>/// DecoratorApp test/// </summary>public class DecoratorApp{ public static void Main( string[] args ) { // Create book and video and display Book book = new Book( "Schnell", "My Home", 10 ); Video video = new Video( "Spielberg", "Schindler's list", 23, 60 ); book.Display(); video.Display(); // Make video borrowable, then borrow and display Console.WriteLine( " Video made borrowable:" ); Borrowable borrowvideo = new Borrowable( video ); borrowvideo.BorrowItem( "Cindy Lopez" ); borrowvideo.BorrowItem( "Samuel King" ); borrowvideo.Display(); }}
Vi. advantages and disadvantages of using the decoration Mode
The decoration mode has the following advantages:
The purpose of the relationship between the decoration mode and inheritance is to expand the object functions, but the decoration mode can provide more flexibility than inheritance.
By using different specific decorative classes and the arrangement and combination of these decorative classes, designers can create a combination of many different behaviors.
This is more flexible than inheritance, and it also means that the decoration mode is more error-prone than inheritance.
The decoration mode has the following Disadvantages:
Because the decorative mode is used, there are a few categories that are needed than the inheritance relationship. Use fewer classes, of course, make the design easier. However, on the other hand, the use of decoration mode will produce more objects than the use of inheritance relationships. More objects make it difficult to query errors, especially those objects look very similar.
7. Discussion on model implementation
In most cases, the implementation of the decoration mode is simpler than the schematic implementation given in the above definition. Note the following when simplifying the mode:
(1) the interface of a decoration class must be compatible with the interface of the decoration class.
(2) Try to keep Component as a "light" class. Do not put too much logic and status in the Component class.
(3) If there is only one ConcreteComponent class but no abstract Component class (Interface), the Decorator class can often be a subclass of ConcreteComponent. As shown in:
(4) If there is only one ConcreteDecorator class, there is no need to create a separate Decorator class. Instead, you can combine the responsibilities of Decorator and ConcreteDecorator into one class.
VIII. Transparency requirements
Transparent decoration Mode
The decoration mode usually requires abstract programming. The transparency of the decoration mode on the client requires that the program declare a variable of the ConcreteDecorator type instead of a variable of the Component type. In other words, the following method is correct:
Component c = new ConcreteComponent();Component c1 = new ConcreteDecorator1(c);Component c2 = new ConcreteDecorator(c1);
The following method is incorrect:
ConcreteComponent c = new ConcreteDecorator();
This is what we mentioned above. The decoration mode is completely transparent to the client.
In the example of Sun Wukong, all the changes made by Sun Wukong must always be treated as Sun Wukong. If he regards the old grandson as a child rather than a child, he will be cheated by the old grandson, this should not happen.
The following method is incorrect:
Grand Saint c =NewGrand sage (); sparrow bird =NewQue Er (c );
Translucent decoration Mode
However, the pure decoration mode is hard to find. The purpose of the decoration mode is to enhance the performance of the classes to be considered without changing the interface. When enhancing performance, new public methods are often required. Even in Sun dasheng's system, new methods are needed. For example, qitian dasheng does not have the flight capability, but queer does. This means that que should have a new fly () method.
This leads to the implementation of most decoration modes being "semi-transparent" rather than completely "transparent. In other words, allow the decoration mode to change the interface and add new methods. Declare the variables of the ConcreteDecorator type, so that you can call the methods in the ConcreteDecorator class:
Qi tianda c =NewGrand sage (); sparrow bird =NewQue Er (c); bird. fly ();
The Qi tiandasheng interface does not have the fly () method at all, but the que ER interface does.
9. Application of decoration mode in. NET
. Net has the following class model:
The following code snippet is used to output the content format of XmlDocument. We can understand the role of the Decorator mode.
Copy and save
// Generate ConcreteComponent (memory stream ms)MemoryStream MS =NewMemoryStream ();// Use XmlTextWriter to decorate the memory stream MS// The translucent decoration mode is used hereXmlTextWriter xtw =NewXmlTextWriter (MS, Encoding. UTF8); xtw. Formatting = Formatting. Indented;// The operation on the decoration xtw will switch to the Ontology-memory stream msXmlDoc. Save (xtw );Byte[] Buf = ms. ToArray (); txtResult. Text = Encoding. UTF8.GetString (buf, 0, buf. Length); xtw. Close ();