装饰(Decorator)模式是动态地给一个对象添加一些额外的责任。就增加功能来讲,Decorator模式相比生成子类更加灵活。
举个例子,假设我们的游戏里面可以为角色穿衣服,每件衣服有不同的特性,那么可以考虑使用装饰模式来实现这个功能。
代码:
public interface IComponent
{
string GetDescription();
void RecoverHP(int hp);
int MagicDamage();
}
首先定义一个公共的接口IComponent。public class Character : IComponent
{
private string _name;
public int HP { get; private set;}
public Character(string name)
{
_name = name;
HP = 10;
}
public virtual string GetDescription()
{
return string.Format ("Character {0}[{1}]", _name, HP);
}
public virtual void RecoverHP(int hp)
{
HP += hp;
}
public virtual int MagicDamage()
{
return 100;
}
}
这里定义了继承自IComponent的Character类型,并且实现了接口的三个方法。
接着,我们定义衣服类型:
public abstract class Clothing : IComponent
{
protected IComponent _component;
public Clothing(IComponent componet)
{
_component = componet;
}
public virtual string GetDescription()
{
return _component.GetDescription ();
}
public virtual void RecoverHP(int hp)
{
_component.RecoverHP (hp);
}
public virtual int MagicDamage()
{
return _component.MagicDamage ();
}
}
衣服是一个抽象类,它为派生类提供了一些公用的方法。
通过构造函数传入IComponent,可以是Character也可以是Clothing,然后每个方法都实际调用_component的方法。
实现两个派生自Clothing的类型:
public class RedHat : Clothing
{
public RedHat(IComponent componet) : base(componet)
{
}
public override string GetDescription()
{
return base.GetDescription() + " in red hat[Rec + 10%]";
}
public override void RecoverHP(int hp)
{
hp += (int)(hp * 0.1f);
base.RecoverHP (hp);
}
}
public class Robe : Clothing
{
private int _magicPlus;
public Robe(IComponent componet, int magP) : base(componet)
{
_magicPlus = magP;
}
public override string GetDescription ()
{
return base.GetDescription() + string.Format (" in robe[Mag + {0}]", _magicPlus);
}
public override int MagicDamage()
{
return base.MagicDamage () + _magicPlus;
}
}
两个类分别具有不同的特性,RedHat在回复hp的时候额外回复10%,而Robe提供了魔法伤害加成。
使用:
IComponent component = new Robe (new RedHat (new Character ("X")), 10);
component.RecoverHP (10);
Console.WriteLine(component.GetDescription ());
Console.WriteLine (component.MagicDamage ());
输出:
Character X[21] in red hat[Rec + 10%] in robe[Mag + 10]
110
装饰模式用的应用场景:我们有许许多多相互(相对)独立的(小)组件,它们具有不同的功能与特性,可以将它们(理想情况下)任意组合添加到指定的类型实例中去。
装饰模式的好处在于,可以比继承更灵活的扩展功能,而且可以更自由。使用继承可能会导致单个类的功能膨胀,而装饰模式正好避免了这个缺点。
但是这样就有可能会产生许许多多的小类,这将导致程序变得复杂,并且增加排错的难度。而且被装饰的类(例如Character)本身是没有或者拥有不完整的装饰信息,这样可能会导致一些困扰和不方便。
它与建造者模式的区别在于:建造者的建造流程是固定的,而装饰模式的装饰流程是不固定的,并且步骤也是可多可少。然而这也可能会产生问题,因为有些时候我们需要一个固定的先后顺序(例如我需要先戴上小红帽再在头上插上小红花),这样就需要额外的判断来让用户遵守这个顺序。