无废话C#设计模式之十三:Decorator
意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
场景
在设计网络游戏的武器系统时,开始并没有考虑到武器的强化和磨损。之后,策划人员说希望给游戏增加强化系统和修理系统,那么我们的武器类型就需要对外提供强化、磨损、修理等方法了。发生这种改动是我们最不愿意看到的,按照设计原则,我们希望功能的扩展尽可能不要修改原来的程序。你可能会想到使用继承来实现,但是策划人员的需求是有的武器能磨损能修理,不能强化,有的武器能强化,但是不会磨损,有的武器既能强化还能磨损和修理。遇到这样的情况,继承的方案可能不适合了,一来继承的层次可能会很多,二来子类的数量可能会很多。
由此,引入装饰模式来解决这个问题。装饰模式使得我们能灵活赋予类额外的职责,并且使得设计和继承相比更合理。
示例代码
using System; using System.Collections.Generic; using System.Text; namespace DecoratorExample { classProgram { staticvoid Main(string[] args) { Weapon w = newRifle(); w.ShowInfo(); Enhance enhancedWeapon = newEnhance(w); enhancedWeapon.EnhanceAmmo(); enhancedWeapon.ShowInfo(); Wear wornWeapon = newWear(w); wornWeapon.WearByRate(0.8); wornWeapon.ShowInfo(); } } abstractclassWeapon { privatedouble ammo; publicdouble Ammo { get { return ammo; } set { ammo = value; } } privatedouble attack; publicdouble Attack { get { return attack; } set { attack = value; } } privatedouble speed; publicdouble Speed { get { return speed; } set { speed = value; } } privatestring name; publicstring Name { get { return name; } set { name = value; } } publicabstractvoid ShowInfo(); } classRifle : Weapon { public Rifle() { this.Ammo = 100; this.Attack = 10; this.Speed = 5; this.Name = "Rifle"; } publicoverridevoid ShowInfo() { Console.WriteLine(string.Format("ammo"t{0}", Ammo)); Console.WriteLine(string.Format("attack"t{0}", Attack)); Console.WriteLine(string.Format("speed"t{0}", Speed)); Console.WriteLine(string.Format("name"t{0}", Name)); } } abstractclassDecorator : Weapon { protectedWeapon w; public Decorator(Weapon w) { this.w = w; } publicoverridevoid ShowInfo() { w.ShowInfo(); } } classEnhance : Decorator { public Enhance(Weapon w) : base(w) { } publicvoid EnhanceAmmo() { w.Ammo += 20; Console.WriteLine(">>>>>>>>>>>>Enhanced"); } } classWear : Decorator { public Wear(Weapon w) : base(w) { } publicvoid WearByRate(double rate) { w.Speed = w.Speed * rate; w.Attack = w.Attack * rate; Console.WriteLine(">>>>>>>>>>>>Worn"); } } } |
代码执行结果如下图:
代码说明
l Weapon是抽象构件角色。
l Rifle是具体构件角色,实现抽象构件的接口。
l Decorator是装饰角色。装饰角色有两个特点,一是继承了抽象构件的接口,二是有一个构件角色的实例。
l Enhance和Wear是具体装饰角色,它们负责给构件附加责任。
l 客户端在使用装饰角色的时候并没有针对抽象构件进行编程,因为我们确实需要使用具体装饰角色提供的额外方法,这种类型的装饰叫做半透明装饰。
何时采用
l 从代码角度来说,如果你觉得由于功能的交叉扩展不会导致非常多的子类或者非常多的继承层次的话可以考虑装饰模式。
l 从应用角度来说,如果你希望动态给类赋予或撤销一些职责,并且可以任意排列组合这些职责的话可以使用装饰模式。
实现要点
l 让装饰角色还继承抽象构件角色也是装饰模式最大的特点,目的就是给抽象构件增加职责,对外表现为装饰后的构件。
l 让装饰角色拥有构件角色实例的目的就是让构件能被多个装饰对象来装饰。
l 在具体应用中可以灵活一点,不一定要有抽象构件和装饰角色。但是,装饰对象继承装饰对象并且拥有它实例的两大特点需要体现。
l 透明装饰一般通过在基类方法前后进行扩充实现,半透明装饰一般通过新的接口实现。
注意事项
l 装饰模式和桥接模式的区别是,前者是针对功能的扩展,本质上还是一样东西,而后者针对多维护变化。装饰模式的思想在于扩展接口而桥接模式的思想是分离接口。
l 装饰类可能会比较琐碎,并且不利于复用,装饰模式在增加了灵活性的同时也降低了封装度,在实际应用中可以和其它模式配合。