DOTA中的设计模式(3):装饰者模式
一、概念
装饰者模式(Strategy):它动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。(原文:The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.)
二、类图

三、存储记忆:在你的大脑里记下类图,同时理解以下对象的职责与行为
(1)Component(被装饰对象基类)
定义对象的接口,可以给这些对象动态增加职责;
(2)ConcreteComponent(具体被装饰对象)
定义具体的对象,Decorator可以给它增加额外的职责;
(3)Decorator(装饰者抽象类)
维护一个指向Component实例的引用,并且定义了与Component一致的接口;
(4)ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象增加具体的职责;
四、DOTA游戏
(1)在这款游戏中,英雄可以佩戴各种各样的装备,随着装备的不同英雄各项属性也不同。这是一个典型的装饰者模式的原型。

(2)英雄可以佩戴不同的装备,而不同装备会增加英雄额外的一些效果,比如,增加攻击力,增加血量,增加魔法等等。根据装饰者模式的定义,我们程序的类图如下:
类图...随后补充。
(3)首先我们设计一个被装饰对象基类:Hero
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
/// <summary> /// 英雄抽象类 /// </summary> public abstract class Hero { #region ==== 公有属性 ==== /// <summary> /// 名称 /// </summary> public abstract string Name { get; } /// <summary> /// 昵称 /// </summary> public string NickName { get; set; } /// <summary> /// 血量 /// </summary> public virtual int Blood { get; set; } /// <summary> /// 魔法 /// </summary> public virtual int Magic { get; set; } /// <summary> /// 攻击力 /// </summary> public virtual int AttackForce { get; set; } #endregion #region ==== 公有方法 ==== /// <summary> /// 获得能力值 /// </summary> /// <returns></returns> public void GetAbilityValue() { Console.WriteLine(string.Format("【{0}】英雄【{1}】裸奔|血量:{2},魔法{3},攻击力{4}",Name, NickName, Blood, Magic, AttackForce)); } #endregion |
(4)我们实现了一个具体被装饰对象:Traxex
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/// <summary> /// 暗夜游侠 /// </summary> public class Traxex:Hero { #region ==== 构造函数 ==== public Traxex() { this.Blood = 500; this.Magic = 300; this.AttackForce = 40; } #endregion #region ==== 接口实现 ==== public override string Name { get { return "暗夜游侠"; } } #endregion |
(5)已经构建完被装饰对象后,我们构建了一个装饰对象的抽象类,这个类需要继承自被装饰对象基类,以保证装饰者与被装饰者的类型匹配:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/// <summary> /// 装饰对象基类 /// </summary> public abstract class EquipDecorator:Hero { #region ==== 公有方法 ==== /// <summary> /// 获得能力值 /// </summary> /// <returns></returns> public abstract void GetAbilityValue(); #endregion } |
(6)根据装饰者,我们实现了四个具体装饰者:E攻击之爪、E活力之球、E精气之球、E能量之球
a. E攻击之爪(其它代码实现类似,暂略,详见源码)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
/// <summary> /// 攻击之爪 /// </summary> public class E攻击之爪:EquipDecorator { #region ==== 私有字段 ==== Hero hero; #endregion #region ==== 构造函数 ==== /// <summary> /// 构造函数 /// </summary> public E攻击之爪() { this.AttackForce = 9; } /// <summary> /// 构造函数 /// </summary> /// <param name="hero">英雄</param> public E攻击之爪(Hero hero) { this.hero = hero; this.AttackForce = 9; hero.Blood = hero.Blood; hero.AttackForce += AttackForce; this.NickName = hero.NickName; this.Blood = hero.Blood; this.Magic = hero.Magic; this.AttackForce = hero.AttackForce; Console.WriteLine(string.Format("英雄【{0}】装备了{1}|血量:{2},魔法{3},攻击力{4}", hero.NickName, this.Name, hero.Blood, hero.Magic, hero.AttackForce)); } #endregion #region ==== 接口实现 ==== public override string Name { get { return "攻击之爪"; } } public override void GetAbilityValue() { Console.WriteLine(string.Format("装备功能:增加基础攻击力{0}点",this.AttackForce)); } #endregion } |
(7)测试程序如下:
a. 测试程序:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/// <summary> /// 测试程序 /// </summary> class Program { static void Main(string[] args) { //t小黑1装备了:能量之球,精气之球,E活力之球,E攻击之爪 Hero t小黑1 = new Traxex(); t小黑1.NickName = "地瓜"; t小黑1.GetAbilityValue(); t小黑1 = new E攻击之爪(new E活力之球(new E精气之球(new E能量之球(t小黑1)))); //小黑2装备了:2个E精气之球,1个E攻击之爪 Hero t小黑2 = new Traxex(); t小黑2.NickName = "土豆"; t小黑2.GetAbilityValue(); t小黑2 = new E攻击之爪(new E精气之球(new E精气之球(t小黑2))); } } |
b. 测试结果:

五、应用场景和优缺点
(1) 应用场景
a. 想透明并且动态地给对象增加新的职责的时候。
b. 给对象增加的职责,在未来存在增加或减少可能。
c. 用继承扩展功能不太现实的情况下,应该考虑用组合的方式。
(2) 优缺点
a. 通过组合而非继承的方式,实现了动态扩展对象的功能的能力[优点]
b. 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题[优点]
c. 充分利用了继承和组合的长处和短处,在灵活性和扩展性之间找到完美的平衡点[优点]
d. 装饰者和被装饰者之间虽然都是同一类型,但是它们彼此是完全独立并可以各自独立任意改变的[优点]
e. 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合[优点]
f. 装饰链不能过长,否则会影响效率[缺点]
g. 因为所有对象都是Component,所以如果Component内部结构发生改变,则不可避免地影响所有子类(装饰者和被装饰者),也就是说,通过继承建立的关系总是脆弱
地,如果基类改变,势必影响对象的内部,而通过组合(Decoator HAS A Component)建立的关系只会影响被装饰对象的外部特征[缺点]
h. 只在必要的时候使用装饰者模式,否则会提高程序的复杂性,增加系统维护难度[缺点]

出处:http://traxex.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号