设计模式学习之路——Decorator装饰模式(结构模式)
子类复子类,子类何其多
假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等。
动机(Motivation)
上述描述的问题根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?
意图(Intent)
动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。
——《设计模式》GoF
结构(Structure)
代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | using System; namespace Decorator.Structural { /// <summary> /// MainApp startup class for Structural /// Decorator Design Pattern. /// </summary> class MainApp { static void Main() { // 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(); // Wait for user Console.ReadKey(); } } /// <summary> /// The 'Component' abstract class /// </summary> abstract class Component { public abstract void Operation(); } /// <summary> /// The 'ConcreteComponent' class /// </summary> class ConcreteComponent : Component { public override void Operation() { Console.WriteLine( "ConcreteComponent.Operation()" ); } } /// <summary> /// The 'Decorator' abstract class /// </summary> abstract class Decorator : Component { protected Component component; public void SetComponent(Component component) { this .component = component; } public override void Operation() { if (component != null ) { component.Operation(); } } } /// <summary> /// The 'ConcreteDecoratorA' class /// </summary> class ConcreteDecoratorA : Decorator { public override void Operation() { base .Operation(); Console.WriteLine( "ConcreteDecoratorA.Operation()" ); } } /// <summary> /// The 'ConcreteDecoratorB' class /// </summary> class ConcreteDecoratorB : Decorator { public override void Operation() { base .Operation(); AddedBehavior(); Console.WriteLine( "ConcreteDecoratorB.Operation()" ); } void AddedBehavior() { } } } |
输出:
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | class Program { static void Main( string [] args) { // Create book Book book = new Book( "Worley" , "Inside ASP.NET" , 10); book.Display(); // Create video Video video = new Video( "Spielberg" , "Jaws" , 23, 92); video.Display(); // Make video borrowable, then borrow and display Console.WriteLine( "\nMaking video borrowable:" ); Borrowable borrowvideo = new Borrowable(video); borrowvideo.BorrowItem( "Customer #1" ); borrowvideo.BorrowItem( "Customer #2" ); borrowvideo.Display(); // Wait for user Console.ReadKey(); } } /// <summary> /// The 'Component' abstract class /// </summary> abstract class LibraryItem { private int _numCopies; // Property public int NumCopies { get { return _numCopies; } set { _numCopies = value; } } public abstract void Display(); } /// <summary> /// The 'ConcreteComponent' class /// </summary> class Book : LibraryItem { private string _author; private string _title; // Constructor public Book( string author, string title, int numCopies) { this ._author = author; this ._title = title; this .NumCopies = numCopies; } public override void Display() { Console.WriteLine( "\nBook ------ " ); Console.WriteLine( " Author: {0}" , _author); Console.WriteLine( " Title: {0}" , _title); Console.WriteLine( " # Copies: {0}" , NumCopies); } } /// <summary> /// The 'ConcreteComponent' class /// </summary> class Video : LibraryItem { 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; } public override void Display() { Console.WriteLine( "\nVideo ----- " ); Console.WriteLine( " Director: {0}" , _director); Console.WriteLine( " Title: {0}" , _title); Console.WriteLine( " # Copies: {0}" , NumCopies); Console.WriteLine( " Playtime: {0}\n" , _playTime); } } /// <summary> /// The 'Decorator' abstract class /// </summary> abstract class Decorator : LibraryItem { protected LibraryItem libraryItem; // Constructor public Decorator(LibraryItem libraryItem) { this .libraryItem = libraryItem; } public override void Display() { libraryItem.Display(); } } /// <summary> /// The 'ConcreteDecorator' class /// </summary> class Borrowable : Decorator { protected List< string > borrowers = new List< string >(); // Constructor public Borrowable(LibraryItem libraryItem) : base (libraryItem) { } 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: " + borrower); } } } |
输出:
Decorator模式的几个要点
- 通过采用组合、而非继承的手法, Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。
- Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。
- Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。
- Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步