c#装饰器模式详解
基础介绍:
动态地给一个对象添加一些额外的职责。适用于需要扩展一个类的功能,或给一个类添加多个变化的情况。
装饰器,顾名思义就是在原有基础上添加一些功能。
大家都只知道如果想单纯的给原有类增加一些功能,可以直接继续该类生成一个子类就可以。
举个例子,如果现在有个手机类,想给手机贴膜,传统的做法就是新建一个手机类的子类(手机贴膜子类),继承自手机类。
使用这个子类就可以完成对手机的贴膜操作。
那如果又想给手机按保护壳的话,传统做法有两种,可以继续新建一个手机类的子类(手机保护壳子类),继承自手机类。
使用这个子类可以给手机按保护壳,但也就失去了给手机贴膜的功能。另一种做法,新建一个手机贴膜类的子类(手机贴膜+保护壳),也就是手机类的子子类。
这样即可以贴膜也可以按手机壳。
大家思考一个问题,如果有很多个装饰并且想随意组合的话,那就有N个子类并且存在很深的继承链路。
想要解决这个问题,就可以用到装饰器了。
比如贴膜装饰、保护壳装饰、贴纸装饰等等,它们都是独立存在的,只继承自装饰器类。
什么意思呢?就是说给手机贴膜的时候它并不会给手机按保护壳的功能,职责单一,贴膜装饰器只负责给手机贴膜。
这样做有什么好处呢?好处就是这些装饰可以随意组合,比如即想贴膜又想按保护壳,就可以将贴膜装饰+保护壳装饰进组组合。
在装饰器模式中各个角色有:
- 抽象构件(Component)角色:规范手机的构成
- 具体构件(ConcreteComponent)角色:继承自抽象构件,具体实现手机。(大多情况下,可以省略抽象构件,抽象装饰类可以直接继承)
- 抽象装饰类(Decorator)角色:创建一个构件(Component)对象的实例,可以使用这个实例调用原构件的功能,并规范装饰类。
- 具体装饰类(ConcreteDecorator)角色:继承自抽象装饰类,具体实现该装饰功能。
应用场景:
原有类无法修改或者修改困难的情况下,对类进行多次扩展或功能性比较相互独立,有效防止多次扩展的情况下子类的膨胀。
注:如果类的扩展比较简单,并且不会多次进行扩展的情况下直接使用类的继承生成子类的方式更为方便快捷。
创建方式:
为了方便说明,以下实例就不创建抽象构件了。
-
首先先看下不使用装饰器进行类的扩展
1 /// <summary> 2 /// 手机类 3 /// </summary> 4 public class Phone 5 { 6 public void Print() 7 { 8 Console.WriteLine("手机"); 9 } 10 } 11 12 /// <summary> 13 /// 手机贴膜 14 /// </summary> 15 public class Sticker : Phone 16 { 17 public Sticker() 18 { 19 base.Print(); 20 } 21 22 /// <summary> 23 /// 进行贴膜 24 /// </summary> 25 public void AddSticker() 26 { 27 Console.WriteLine("给手机贴膜"); 28 } 29 } 30 31 /// <summary> 32 /// 手机保护壳 33 /// </summary> 34 public class ProtectiveCase : Phone 35 { 36 public ProtectiveCase() 37 { 38 base.Print(); 39 } 40 41 /// <summary> 42 /// 按保护壳 43 /// </summary> 44 public void AddProtectiveCase() 45 { 46 Console.WriteLine("给手机按保护壳"); 47 } 48 } 49 50 /// <summary> 51 /// 即贴膜又按保护壳 52 /// </summary> 53 public class ProtectiveCaseAndSticker : Sticker 54 { 55 public ProtectiveCaseAndSticker() 56 { 57 } 58 59 /// <summary> 60 /// 按保护壳 61 /// </summary> 62 public void AddProtectiveCase() 63 { 64 Console.WriteLine("给手机按保护壳"); 65 } 66 } 67 68 /// <summary> 69 /// 客户端 70 /// </summary> 71 class Client 72 { 73 static void Main(string[] args) 74 { 75 //创建一个手机 76 Phone phone = new Phone(); 77 phone.Print(); 78 Console.WriteLine("\r\n"); 79 80 //给手机贴膜 81 Sticker sticker = new Sticker(); 82 sticker.AddSticker(); 83 Console.WriteLine("\r\n"); 84 85 //给手机按保护壳 86 ProtectiveCase protectiveCase = new ProtectiveCase(); 87 protectiveCase.AddProtectiveCase(); 88 Console.WriteLine("\r\n"); 89 90 //即贴膜又按保护壳 91 ProtectiveCaseAndSticker protectiveCaseAndSticker = new ProtectiveCaseAndSticker(); 92 protectiveCaseAndSticker.AddSticker(); 93 protectiveCaseAndSticker.AddProtectiveCase(); 94 Console.ReadKey(); 95 } 96 }
通过上述实例可以看出,如果各个扩展功能比较独立的话可以直接进行继承扩展。
如果各个功能直接有交集的情况下,会造成很深的继承关系。
比如上述实例中,如果单独贴膜或者单独安装保护壳则直接继承手机类即可。
但如果想要即贴膜又要安装保护壳,各自继承手机类的方式就行不通了,只能在贴膜类或者保护壳类的基础上进行扩展。
如果还有添加手机挂饰,那就还需要再一层继承关系。
要解决这个问题就用到了装饰器,下面看看使用装饰器是怎么给手机添加新功能的。
-
使用装饰器模式对类进行扩展
1 /// <summary> 2 /// 手机类 3 /// </summary> 4 public class Phone 5 { 6 public void Print() 7 { 8 Console.WriteLine("手机"); 9 } 10 } 11 12 /// <summary> 13 /// 装饰抽象类 14 /// </summary> 15 public abstract class Decorator : Phone 16 { 17 private Phone phone; 18 19 public Decorator(Phone p) 20 { 21 this.phone = p; 22 } 23 24 public abstract void AddDecorator(); 25 } 26 27 /// <summary> 28 /// 贴膜装饰 29 /// </summary> 30 public class Sticker : Decorator 31 { 32 public Sticker(Phone p) 33 : base(p) 34 { 35 } 36 37 public override void AddDecorator() 38 { 39 Console.WriteLine("给手机贴膜"); 40 } 41 } 42 43 /// <summary> 44 /// 保护壳装饰 45 /// </summary> 46 public class ProtectiveCase : Decorator 47 { 48 public ProtectiveCase(Phone p) 49 : base(p) 50 { 51 } 52 53 /// <summary> 54 /// 按保护壳 55 /// </summary> 56 public override void AddDecorator() 57 { 58 Console.WriteLine("给手机按保护壳"); 59 } 60 } 61 62 /// <summary> 63 /// 客户端 64 /// </summary> 65 class Client 66 { 67 static void Main(string[] args) 68 { 69 //单独给手机贴膜 70 Phone phone = new Phone(); 71 phone.Print(); 72 Decorator sticker = new Sticker(phone); 73 sticker.AddDecorator(); 74 75 //单独给手机按保护壳 76 phone = new Phone(); 77 phone.Print(); 78 Decorator protectiveCase = new ProtectiveCase(phone); 79 protectiveCase.AddDecorator(); 80 Console.WriteLine("\r\n"); 81 82 //即贴膜又按保护壳 83 phone = new Phone(); 84 phone.Print(); 85 //首先创建贴膜装饰实例,将手机对象传入 86 Decorator decorator = new Sticker(phone); 87 //进行贴膜操作 88 decorator.AddDecorator(); 89 //创建保护壳装饰实例,将贴膜后的手机对象传入 90 decorator = new ProtectiveCase(decorator); 91 //进行按保护壳操作 92 decorator.AddDecorator(); 93 Console.ReadKey(); 94 } 95 }
从上述实例中可以看出,各个装饰类只对装饰抽象类负责,职责单一。
各个装饰进行组合时,方便随意。新增装饰时,只需要新增一个继承自装饰抽象类的子类即可实现以原有装饰的随意组合使用。
总结:
想要扩展一个类的时候,传统的继承生成子类的形式,适用于扩展简单,并且不会多次扩展的情况下。
而如果一个类的扩展是周期性,多次扩展的或功能性比较相互独立的情况下,可以使用装饰器,可以有效的解决传统继承扩展子类膨胀的问题。
装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。