小话设计模式(八)装饰模式

本文介绍装饰模式的基本概念及其实现方式,并通过游戏角色穿戴不同装备的例子展示了装饰模式如何灵活扩展对象的功能。

装饰(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)本身是没有或者拥有不完整的装饰信息,这样可能会导致一些困扰和不方便。

它与建造者模式的区别在于:建造者的建造流程是固定的,而装饰模式的装饰流程是不固定的,并且步骤也是可多可少。然而这也可能会产生问题,因为有些时候我们需要一个固定的先后顺序(例如我需要先戴上小红帽再在头上插上小红花),这样就需要额外的判断来让用户遵守这个顺序。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值