修饰模式的UML类图如下:
通过使用装饰模式,可以在运行时动态地扩充一个类的功能。
原理是:增加一个修饰类包裹原来的类。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。
当有几个相互独立的功能需要扩充时,这个区别就变得很重要。在有些面向对象的编程语言中,类不能在运行时被创建,通常在设计的时候也不能预测到有哪几种功能组合。这就意味着要为每一种组合创建一个新类。相反,修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。
其示例性代码:
using System; namespace DoFactory.GangOfFour.Decorator.Structural { /// <summary> /// MainApp startup class for Structural /// Decorator Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> 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() { } } }
下面再给出一个示例程序,来自程杰的大话设计模式:
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 { /// <summary> /// MainApp startup class for Structural /// Observer Design Pattern. /// </summary> 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(); } } /// <summary> /// 装备类,相当于Component /// </summary> public abstract class Equipment { public abstract void Attack(); } /// <summary> /// 士兵类,继承自Equipment /// </summary> public class Soldier : Equipment { public Soldier() { // 构造函数 } /// <summary> /// 没有任何武器装备下的核心功能 /// </summary> public override void Attack() { Console.WriteLine("用拳脚攻击!"); } } public abstract class EquipDecorator : Equipment { protected Equipment equipment; /// <summary> /// 增加装备,使用该方法来动态地给士兵增加装备 /// </summary> /// <param name="equipment"></param> public void SetComponent(Equipment equipment) { this.equipment = equipment; } /// <summary> /// 攻击 /// </summary> public override void Attack() { //如果有装备,就用装备进行攻击 if (equipment != null) { equipment.Attack(); } } } /// <summary> /// 步枪 /// </summary> public class RifleEquipment : EquipDecorator { public override void Attack() { base.Attack(); Console.WriteLine("步枪射击,啪!"); } } /// <summary> /// 机枪 /// </summary> public class MachineGunEquipment : EquipDecorator { public override void Attack() { base.Attack(); Console.WriteLine("机枪扫射,突突突!"); } } /// <summary> /// 火箭筒 /// </summary> public class RocketGunEquipment : EquipDecorator { public override void Attack() { base.Attack(); Console.WriteLine("火箭炮射击,唰......!"); } } }
输出结果:
优点
1 每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。它是由Decorator的SetComponent方法来实现的,因而它们的职责是单一的。
2 类的核心职责与动态添加的职责是分离的。如果再向主类中添加新的功能,一是违反了开放封闭原则,二是增加了主类的复杂度。
3 比静态继承更灵活 与对象的静态继承相比,Decorator模式提供了更加灵活的向对象添加职责的方式,可以使用添加和分离的方法,用装饰在运行时刻增加和删除职责.
缺点
1 产生许多小对象,采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同。
适用场景
1 当需要为已有功能动态地添加更多功能时。
2 类的核心功能无需改变,只是需要添加新的功能时。