【原】从头学习设计模式(五)——装饰者模式
一、引入
当我们想要扩展类的功能的时候,很多情况下会考虑用继承的方法,比如我有一个手机类,只支持打电话,如果我们想要扩展手机的功能,让普通的手机变成智能手机,那最简单的方式就是新建一个智能手机类并继承手机类,扩充智能手机拥有的新功能,比如打飞机,玩愤怒的小鸟之类的。
但是子类继承的方法总归不是非常灵活啊,为了更好的解决类功能扩展的问题,我们来引用今天要学习的新模式——装饰模式。首先先看一下标准定义。
装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。
从这个定义中可以找到几个要点:
1.动态的扩展
2.不会改变原有类
3.不用生成子类的继承方式
在这个模式中主要有四大组件
1.原始类接口(接口类型或者抽象类)
2.原始类的具体实现类
3.装饰器接口(一般类或抽象类)
4.继承了装饰器接口的一系列具体装饰器
对照以上这些组件来看一下类图表示。
二、UML类图
从UML中可以看到:具体实现类和装饰器基类都继承自原始类接口
下面我们来模拟一下将一部普通手机升级为智能手机和山寨智能手机的全过程。
三、代码描述
1.原始类接口可以是interface或者abstract class
1 /// <summary> 2 /// 手机总接口 3 /// </summary> 4 public interface Phone 5 { 6 void Functionality(); 7 }
2.原始类的具体实现类
1 /// <summary> 2 /// 一般的手机类 3 /// </summary> 4 public class CommonPhone : Phone 5 { 6 public void Functionality() 7 { 8 Console.WriteLine("能发短信和打电话."); 9 } 10 }
3. 装饰器接口,可以是普通类(用virtual声明 Functionality() 方法可以被子类重写)或者抽象类(override Functionality() 方法)
1 /// <summary> 2 /// 装饰基类 3 /// </summary> 4 public class PhoneDecorator : Phone 5 { 6 protected Phone phone; 7 8 public PhoneDecorator() 9 { 10 } 11 12 public PhoneDecorator(Phone phone) 13 { 14 this.phone = phone; 15 } 16 17 public virtual void Functionality() 18 { 19 if (phone != null) 20 { 21 phone.Functionality(); 22 } 23 } 24 }
4.具体装饰器,需要重写基类Functionality() 方法,并添加自己的新功能
1 /// <summary> 2 /// 智能机修饰类 3 /// </summary> 4 public class AdvancedPhone : PhoneDecorator 5 { 6 public AdvancedPhone(Phone phone) 7 : base(phone) 8 { 9 10 } 11 12 public override void Functionality() 13 { 14 base.Functionality(); 15 Console.WriteLine("可以玩愤怒的小鸟!"); 16 } 17 18 }
1 /// <summary> 2 /// 山寨机装饰类 3 /// </summary> 4 public class ShanZhaiPhone : PhoneDecorator 5 { 6 public ShanZhaiPhone(Phone phone) 7 : base(phone) 8 { 9 } 10 11 public override void Functionality() 12 { 13 base.Functionality(); 14 Console.WriteLine("可以双卡双待!"); 15 } 16 }
5.功能测试类:
1 private static void Main(string[] args) 2 { 3 Console.WriteLine("-----------------------------普通手机-------------------------------"); 4 Phone commonPhone = new CommonPhone(); 5 commonPhone.Functionality(); 6 7 Console.WriteLine("-----------------------------智能手机-------------------------------"); 8 //将普通手机包一层智能的皮就成了智能手机 9 AdvancedPhone advancedPhone = new AdvancedPhone(commonPhone); 10 advancedPhone.Functionality(); 11 12 Console.WriteLine("-----------------------------山寨手机-------------------------------"); 13 //将普通手机包一层山寨的皮就成了强大的山寨手机 14 ShanZhaiPhone shangZhaiPhone = new ShanZhaiPhone(commonPhone); 15 shangZhaiPhone.Functionality(); 16 17 Console.WriteLine("-----------------------------山寨智能手机-------------------------------"); 18 //将智能手机包一层山寨的皮就成了山寨智能机 19 ShanZhaiPhone shangZhaiAdvancedPhone = new ShanZhaiPhone(new AdvancedPhone(new CommonPhone())); 20 shangZhaiAdvancedPhone.Functionality(); 21 22 Console.ReadKey(); 23 24 }
运行结果:
从这个小例子里可以看得出来,装饰模式其实就是一个包装器,并且是从里到外一层一层的包,是有先后顺序的。同时,各种不同装饰器的组合,可以组合出完全不同的效果来。这种链式的包裹方式又叫作—— 装饰链。
你可以想想Windows中的字体设定——装饰模式 ,这就是一个模式应用的好例子,你可以对字体加粗,倾斜,改字体,改字号,所有单独的功能包裹到一起就合成了这样的字体。
四、总结
装饰模式是一个为已有功能的类动态添加更多功能的一种方式,各个功能的扩展相互独立,可以根据需要随意组合,但组合的顺序有先后。
优点:
1.有效避免了继承方式对扩展对象功能带来的灵活性差的问题
2.通过组合而非继承的方式,实现了动态扩展对象的功能的能力
3.符合高内聚、低耦合的设计思想
缺点:
1.装饰链不能过长,否则影响效率
2.原始类接口不能改变,因为所有装饰器基类继承于原始类接口,类接口变化会导致所有装饰器的修改