Definition
装饰模式的UML类图如下:
一个生动的例子:Pizza(手抓饼)
Component:
namespace DecoratorDemo{ public abstract class Pizza { public string Description { get; set; } public abstract string GetDescription(); public abstract double CalculateCost(); }}
ConcreteComponent:
using System;namespace DecoratorDemo{ public class LargePizza : Pizza { public LargePizza() { Description = "Large Pizza"; } public override string GetDescription() { return Description; } public override double CalculateCost() { return 9.00; } }}using System;namespace DecoratorDemo{ public class MediumPizza : Pizza { public MediumPizza() { Description = "Medium Pizza"; } public override string GetDescription() { return Description; } public override double CalculateCost() { return 6.00; } }}using System;namespace DecoratorDemo{ public class SmallPizza : Pizza { public SmallPizza() { Description = "Small Pizza"; } public override string GetDescription() { return Description; } public override double CalculateCost() { return 3.00; } }}
Decorator
using System;namespace DecoratorDemo{ public class PizzaDecorator : Pizza { protected Pizza _pizza; public PizzaDecorator(Pizza pizza) { _pizza = pizza; } public override string GetDescription() { return _pizza.Description; } public override double CalculateCost() { return _pizza.CalculateCost(); } }}
ConcreteDecorator:
using System;namespace DecoratorDemo{ public class Cheese : PizzaDecorator { public Cheese(Pizza pizza) : base(pizza) { Description = "Cheese"; } public override string GetDescription() { return String.Format("{0}, {1}", _pizza.GetDescription(), Description); } public override double CalculateCost() { return _pizza.CalculateCost() + 1.25; } }}using System;namespace DecoratorDemo{ public class Ham : PizzaDecorator { public Ham(Pizza pizza) : base(pizza) { Description = "Ham"; } public override string GetDescription() { return String.Format("{0}, {1}", _pizza.GetDescription(), Description); } public override double CalculateCost() { return _pizza.CalculateCost() + 1.00; } }}using System;namespace DecoratorDemo{ public class Peppers : PizzaDecorator { public Peppers(Pizza pizza) : base(pizza) { Description = "Peppers"; } public override string GetDescription() { return String.Format("{0}, {1}", _pizza.GetDescription(), Description); } public override double CalculateCost() { return _pizza.CalculateCost() + 2.00; } }}
Program:
using System;namespace DecoratorDemo{ class Program { static void Main(string[] args) { Pizza largePizza = new LargePizza(); largePizza = new Cheese(largePizza); largePizza = new Ham(largePizza); largePizza = new Peppers(largePizza); Console.WriteLine(largePizza.GetDescription()); Console.WriteLine("{0:C2}", largePizza.CalculateCost()); Console.ReadKey(); } }}
DebugLZQ: 手抓饼+奶酪+火腿+辣酱
通过使用装饰模式,可以在运行时动态地扩充一个类的功能。
原理是:增加一个修饰类包裹原来的类。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
当有几个相互独立的功能需要扩充时,这个区别就变得很重要。在有些面向对象的编程语言中,类不能在运行时被创建,通常在设计的时候也不能预测到有哪几种功能组合。这就意味着要为每一种组合创建一个新类。相反,修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。
其示例性代码:
using System;namespace DoFactory.GangOfFour.Decorator.Structural{ ////// MainApp startup class for Structural /// Decorator Design Pattern. /// class MainApp { ////// Entry point into console application. /// 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(); } } ////// The 'Component' abstract class /// abstract class Component { public abstract void Operation(); } ////// The 'ConcreteComponent' class /// class ConcreteComponent : Component { public override void Operation() { Console.WriteLine("ConcreteComponent.Operation()"); } } ////// The 'Decorator' abstract class /// abstract class Decorator : Component { protected Component component; public void SetComponent(Component component) { this.component = component; } public override void Operation() { if (component != null) { component.Operation(); } } } ////// The 'ConcreteDecoratorA' class /// class ConcreteDecoratorA : Decorator { public override void Operation() { base.Operation(); Console.WriteLine("ConcreteDecoratorA.Operation()"); } } ////// The 'ConcreteDecoratorB' class /// class ConcreteDecoratorB : Decorator { public override void Operation() { base.Operation(); AddedBehavior(); Console.WriteLine("ConcreteDecoratorB.Operation()"); } void AddedBehavior() { } }}
下面再给出一个示例程序,来自程杰的大话设计模式:
using System; using System.Collections.Generic; using System.Text; namespace 装饰模式 { class Program { static void Main(string[] args) { Person xc = new Person("小菜"); Console.WriteLine("\n第一种装扮:"); Sneakers pqx = new Sneakers(); BigTrouser kk = new BigTrouser(); TShirts dtx = new TShirts(); pqx.Decorate(xc); kk.Decorate(pqx); dtx.Decorate(kk); dtx.Show(); Console.WriteLine("\n第二种装扮:"); LeatherShoes px = new LeatherShoes(); Tie ld = new Tie(); Suit xz = new Suit(); px.Decorate(xc); ld.Decorate(px); xz.Decorate(ld); xz.Show(); Console.WriteLine("\n第三种装扮:"); Sneakers pqx2 = new Sneakers(); LeatherShoes px2 = new LeatherShoes(); BigTrouser kk2 = new BigTrouser(); Tie ld2 = new Tie(); pqx2.Decorate(xc); px2.Decorate(pqx); kk2.Decorate(px2); ld2.Decorate(kk2); ld2.Show(); Console.Read(); } } //Person类 class Person { public Person() { } private string name; public Person(string name) { this.name = name; } public virtual void Show() { Console.WriteLine("装扮的{0}", name); } } //装饰类 class Finery : Person { protected Person component; //打扮 public void Decorate(Person component) { this.component = component; } public override void Show() { if (component != null) { component.Show(); } } } //具体装饰类 class TShirts : Finery { public override void Show() { Console.Write("大T恤 "); base.Show(); } } class BigTrouser : Finery { public override void Show() { Console.Write("垮裤 "); base.Show(); } } class Sneakers : Finery { public override void Show() { Console.Write("破球鞋 "); base.Show(); } } class Suit : Finery { public override void Show() { Console.Write("西装 "); base.Show(); } } class Tie : Finery { public override void Show() { Console.Write("领带 "); base.Show(); } } class LeatherShoes : Finery { public override void Show() { Console.Write("皮鞋 "); base.Show(); } } }
使用装饰模式需要注意的地方:
◇在发生“类爆炸”的情况下,应及时反思工程的设计;
◇在类中,不要过多的将“是否具有某种装饰”用boolean来表示;
◇Decorator(装饰)模式的关键在于“动态地实现功能扩展”;
◇装饰器的安装顺序很重要,应努力做到装饰器的安装顺序不影响最终的装饰效果。
应用实例:
装备大兵!无任何装备时(核心功能)可以用拳脚搏击;装备了步枪,可以正常射击;装备了重机枪,可以扫射;装备了火箭筒,可以防空。
类图:
代码实现:
using System;namespace DecoratorPattern{ ////// MainApp startup class for Structural /// Observer Design Pattern. /// class MainApp { static void Main(string[] args) { // 定义新兵 Soldier soldier = new Soldier(); // 三种装备 RifleEquipment rifle = new RifleEquipment(); MachineGunEquipment machineGun = new MachineGunEquipment(); RocketGunEquipment rocketGun = new RocketGunEquipment(); // 将三种装备全部交给新兵 rifle.SetComponent(soldier); machineGun.SetComponent(rifle); rocketGun.SetComponent(machineGun); // 攻击,除了拳脚功夫外,新兵还可以使用步枪,机枪,火箭炮.最终执行的是rocketGun.Attack(). rocketGun.Attack(); Console.Read(); } } ////// 装备类,相当于Component /// public abstract class Equipment { public abstract void Attack(); } ////// 士兵类,继承自Equipment /// public class Soldier : Equipment { public Soldier() { // 构造函数 } ////// 没有任何武器装备下的核心功能 /// public override void Attack() { Console.WriteLine("用拳脚攻击!"); } } public abstract class EquipDecorator : Equipment { protected Equipment equipment; ////// 增加装备,使用该方法来动态地给士兵增加装备 /// /// public void SetComponent(Equipment equipment) { this.equipment = equipment; } ////// 攻击 /// public override void Attack() { //如果有装备,就用装备进行攻击 if (equipment != null) { equipment.Attack(); } } } ////// 步枪 /// public class RifleEquipment : EquipDecorator { public override void Attack() { base.Attack(); Console.WriteLine("步枪射击,啪!"); } } ////// 机枪 /// public class MachineGunEquipment : EquipDecorator { public override void Attack() { base.Attack(); Console.WriteLine("机枪扫射,突突突!"); } } ////// 火箭筒 /// public class RocketGunEquipment : EquipDecorator { public override void Attack() { base.Attack(); Console.WriteLine("火箭炮射击,唰......!"); } }}
输出结果:
优点
1 每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。它是由Decorator的SetComponent方法来实现的,因而它们的职责是单一的。
2 类的核心职责与动态添加的职责是分离的。如果再向主类中添加新的功能,一是违反了开放封闭原则,二是增加了主类的复杂度。
3 比静态继承更灵活 与对象的静态继承相比,Decorator模式提供了更加灵活的向对象添加职责的方式,可以使用添加和分离的方法,用装饰在运行时刻增加和删除职责.
缺点
1 产生许多小对象,采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同。
适用场景
1 当需要为已有功能动态地添加更多功能时。
2 类的核心功能无需改变,只是需要添加新的功能时。