DOTA版设计模式——装饰模式[Decorator Pattern]
先来点俗的吧,我们来看下装饰模式的UML图:
初始魔法值=智力*13
HeadFirst说的好,封装变化的部分,面向接口编程,多用组合少用继承等等。
dota里重要的是什么?英雄?NO,是兵线。 但这篇装饰者模式的主角是英雄。
一.被装饰者和装饰着需要有一个公共接口。
首先声明一个接口(也就是UML中的Component)用来获得英雄的总生命值,接口很简单只有一个GetHP方法(对应UML中的Operation方法)。
public interface IHero
{
int GetHP();
}
二.实现我们的被装饰者,也就是Dota中的英雄
下面我们的英雄要出场了, 灵魂守卫(Terrorblade)和德鲁伊(Syllabear),这两个英雄都没用过,汗。。。。
(注: LandpyForm.Form.OutputResult是我自己写的输出方法,我写的所有模式都封装在同一个Dll,该方法主要解决在Dll中输出结果显示在UI层的RichTextBox控件中。所有模式介绍完后我会放出完整代码,目前代码都编写完毕,就差我的吹牛文章了。)
/// <summary>
/// 英雄
/// </summary>
public abstract class Hero : IHero
{
protected int _hP;
protected int _power;
public int Power
{
get { return _power; }
}
public int GetHP()
{
return _hP;
}
}
我们的英雄少了很多属性,智力,敏捷。。。。但是他的确是个英雄,嘿嘿 。细心的同学一定会发现我们和UML图有些出入,UML里被装饰者只有一个,而我们确是定义了一个Hero抽象类,其实。。。我是为了能实现更多的英雄。现在我们的英雄有“力量”“HP”。下面该实现我们的英雄了。等待其他玩家中~~~~~
/// <summary>
/// </summary>
public class Terrorblade : Hero
{
public Terrorblade()
{
LandpyForm.Form.OutputResult("创建灵魂守卫");
_power = 15;
_hP = _power * 19 + 150;
}
}
/// <summary>
/// 德鲁伊
/// </summary>
public class Syllabear : Hero
{
public Syllabear()
{
LandpyForm.Form.OutputResult("创建德鲁伊");
_power = 17;
_hP = _power * 19 + 150;
}
}
还记得我们的公式吗? 初始生命值=力量*19+150,在我们的英雄里就是_hP = _power * 19 + 150;当然两个英雄都存在同样的初始化感兴趣的同学可以把公式提取到基类里,我的英雄是abstract所以我没有。。。。。这只是初始化我们英雄的数据它并不重要。
英雄有了,下一步干什么,当然是刷钱搞装备了,英雄没有了装备是件多么可怕的事情。下面来两个装备,活力之球和贵族圆环,我们要用他们装饰我们的英雄:
三. 实现我们的装饰者,也就是Dota中的各种装备
/// <summary>
/// 装备
/// </summary>
public abstract class Equipment : IHero
{
protected IHero hero;
protected int _hp;
protected int _power;
public int GetHP()
{
return _hp + _power * 19 + hero.GetHP();
}
}
/// <summary>
/// 活力之球
/// </summary>
public class VitalityBall : Equipment
{
public VitalityBall(IHero hero)
{
_hp = 200;
_power = 0;
this.hero = hero;
}
}
/// <summary>
/// 贵族圆环
/// </summary>
public class CircletOfNobility : Equipment
{
public CircletOfNobility(IHero hero)
{
_hp = 0;
_power = 2;
this.hero = hero;
}
}
对照一下UML,发现装备Equipment 是UML中的装饰者(Decorater),它是抽象的。而活力之球和贵族圆环派生于装备是我们真正需要的东东(也就是ConcreteComponentA&ConcreteComponentB)。
英雄有了,装备有了,下面我们该用装备打扮(装饰)一下我们可爱的英雄了。我们的关键在于英雄和装备都继承了IHero接口,也就是说在IHero方面两个东东是没有区别的,也就是说对于GetHP方法两者具有统一性。
四。测试我们的代码
DotaPatternLibrary.Decorator.IHero hero = new DotaPatternLibrary.Decorator.Syllabear();
LandpyForm.Form.OutputResult("生命:" + hero.GetHP().ToString());
这时候会输出:
创建德鲁伊
生命:473。
在这里首先用创建一个hero,注意hero是用IHero声明的,就是我们上面提到的面向接口编程,当然这个接口不单指interface如果你用abstract类同样是面向接口编程。好处在于hero没有被固定,我可以用灵魂守卫来实例化他,同样也可以用德鲁伊。
hero = new DotaPatternLibrary.Decorator.Terrorblade();
LandpyForm.Form.OutputResult("生命:" + hero.GetHP().ToString());
还记得我们的hero吗,那个接口对象,我们这次把它实例成灵魂守卫了。
这时候会输出:
创建灵魂守卫
生命:435
正补反补过一部分兵时你应该去买装备了,毕竟我们的英雄需要装备。
LandpyForm.Form.OutputResult("购买活力之球");
hero = new DotaPatternLibrary.Decorator.VitalityBall(hero);
LandpyForm.Form.OutputResult("生命:" + hero.GetHP().ToString());
看看我们的装饰者,我觉得装饰者的魅力就在于此,hero还是那个hero,可是经过活力之球包装后hero的HP就增长了200,我的天,这是真的吗?
仔细看hero = new DotaPatternLibrary.Decorator.VitalityBall(hero)这里我们用new DotaPatternLibrary.Decorator.VitalityBall类包装了我们的hero,同样调用hero.GetHP()出现了什么?一下是结果:
购买活力之球
生命:635
我们的灵魂守卫增长了200的血量,太了不起了,一切都是我们的装饰模式的功劳,可是究竟是怎么实现的那,让我们看看代码吧,下面到了验证奇迹的时刻,希望刘谦不要知道我盗版他的口头禅:)
Equipment中实现了IHero接口方法GetHP,但是它的实现和英雄的实现有了差别,英雄直接就返回_hp字段了,而我们的装饰者不传进来的血量累加了,return _hp + _power * 19 + hero.GetHP(),吼吼,仔细看看我们的代码吧。
注意这里 new DotaPatternLibrary.Decorator.VitalityBall(hero);我们用活力之球(VitalityBall)包装了我们的英雄,然后再赋值给我们的英雄,现在我们的英雄就不是在裸奔了,他有了活力之球,多了200的生命耶。
LandpyForm.Form.OutputResult("购买贵族头环");
hero = new DotaPatternLibrary.Decorator.CircletOfNobility(hero);
LandpyForm.Form.OutputResult("生命:" + hero.GetHP().ToString());
同样我们用贵族圆环装饰他会得到同样的效果哦,现在我们的灵魂守卫有了活力之球和贵族头环两个好东东了,太牛了呃。
墙外音:汗,你菜鸟吧,灵魂出活力之球?我倒
不管怎么样,我们的装饰模式基本成型了,我没有官方定义,只有UML图,一切只能意会。如果有需要了解装饰模式的同学请Call Gof四人组,他们一定会让同学们满意的。呵呵~~~~~~
装饰模式
ag