装饰模式 - 黑胡子
![](/images/decorator.png)
装饰模式 - 黑胡子
苏丙榅1. 马歇尔·D·蒂奇
在海贼世界中,马歇尔·D·蒂奇
绰号黑胡子
,他是黑胡子海贼团的提督、新世界四皇之一
,自然系暗暗果实
和超人系震震果实
能力者,据说他还可以吃下第三个恶魔果实(就目前剧情而言尚未盖棺定论)。
对于黑胡子来说,它拥有的恶魔果实能力并不是与生俱来的,也就是后天获得,恶魔果实能力是对黑胡子原有实力的加成。黑胡子获得的这种恶魔果实能力和设计模式中的装饰模式差不多,都是动态的给一个对象绑定额外的属性(比如:能力、职责、功能等)。
关于装饰模式也可以称之为封装模式,所谓的封装就是在原有行为之上进行拓展,并不会改变该行为,看下面的例子:
- 在进行网络通信的时候,数据是基于IOS七层或四层网络模型(某些层合并之后就是四层模型)进行传输,通过下图可得知从应用层到物理层,数据每向下走一层就会被封装一层,最后将封装好的数据以比特流的方式发送给接收端。
封装之后数据只是变得更复杂了, 并没有改变它是数据的本质。
- A端和B端进行网络通信,默认数据是在网络环境中裸奔的,如果想要对数据进行装饰(也就是封装)就可以在发送数据之前对其加密,接收端收到数据之后对其解密。
加解密是对数据的装饰,但是没有改变数据的本质。
- 平时都是穿长衣长裤,疫情来袭之后穿上了防护服。
防护服是对人的装饰,没有改变本体是人的本质。
上述例子中都是对原有行为进行了拓展,但是并没有改变原有行为,就好比饿了去煮面条,为了使其更美味最终会对其进行装饰,做成打卤面、炸酱面、热干面、油泼面等
,不论怎么处理最终得到的还是面条,最终不可能得到一锅酱牛肉,大方向是不会变的。
2. 解构黑胡子
从概念上对装饰模式有了一定的了解之后,继续分析黑胡子的个人战斗力,根据文章开头对他的介绍,可以知道他的能力来自三个不同的方向:
- 与生俱来加上后天努力练就的本领
- 来自自然系·暗暗果实的能力
- 来自超人系·震震果实的能力
所以这两个恶魔果实的能力就是对黑胡子个人战力的装饰(加成)。另外,还需要明确一点,对于恶魔果实来说不是只有黑胡子能吃,谁吃了都会拥有对应的果实能力,这样这个人的战斗力也就提升了。
2.1 战魂
人自身是拥有战斗力的,而恶魔果实又可以给人附加战斗力,所以我们就可以定义一个战士的抽象类(这个抽象类不能被实例化)。
1 | // 战士的抽象类 |
有了这个抽象类就可以对某个人,或者某个恶魔果实的战力进行具体的实现,也就是说它们需要继承这个抽象类。
- 所有的战士都可以战斗 –
fight()
- 所有的战士都有自己的名字
- 设置名字 – 构造函数
Soldier(string name)
- 获取名字 –
string getName()
- 设置名字 – 构造函数
2.2 黑胡子
上面的战士是一个抽象类,如果想要对它进行实例化就需要写一个子类继承这个抽象类,下面我们定义一个黑胡子类:
1 | // 黑胡子(Marshall·D·Teach) |
在黑胡子类中主要是重写了从父类继承的纯虚函数,这样黑胡子这个类就可以被实例化,得到对应的黑胡子对象了。
2.3 附魔
如果黑胡子想要让自己的实力再提升一个层次,就需要得到外部力量的辅助,可行的方案就是吃恶魔果实,这样就相当于给自己附魔了。恶魔果实的作用是对战士的战力进行装饰,使其战力得到大大的提升,所以恶魔果实类也可以先继承战士这个类。
1 | // 抽象的恶魔果实 |
上面的恶魔果实类DevilFruit
继承了战士类Soldier
之后还是一个抽象类,关于这个类有以下几点需要说明:
- 在
DevilFruit 类
中没有重写父类Soldier
的纯虚函数fight()
,所以它还是抽象类 - 恶魔果实有很多种类,每种恶魔果实能力不同,所以战斗方式也不同,因此需要在恶魔果实的子类中根据每种果实能力去重写作战函数
fight()
的行为。 - 恶魔果实
DevilFruit 类
的作用是给某个Soldier
的子类对象附魔,所以在类内部提供了一个附魔函数enchantment(Soldier* soldier)
,参数就是即将要得到恶魔果实能力的那个战士。
2.4 群魔乱舞
黑胡子目前一共吃下了两颗恶魔果实:自然系暗暗果实
和超人系震震果实
,所以需先定义两个恶魔果实的子类:
1 | // 暗暗果实 |
关于这两个恶魔果实子类需要说明以下几点:
- 在重写父类的
fight()
函数的时候,用当前恶魔果实能力和战士的自身能力进行了加成,调用了战士对象的作战函数m_human->fight()
,在原有基础上提升了其战斗力。 - 在两个恶魔果实子类中,可以根据实际需要定义类独有的方法,比如:
DarkFruit 类中有 warning() 方法,QuakeFruit 类中却没有
。 - 再次强调,这两个子类都继承了父类的附魔函数
enchantment(Soldier* soldier)
,这样就可以完成对战士战力的加成(装饰)了。
假设黑胡子确实是可以吃下第三个恶魔果实,并且发现了一颗神奇的超人系恶魔果实大饼果实
,可以将身边的一切物体变成大饼,帮助自己和队友快速回血。
1 | // 大饼果实 |
使用装饰模式,可以非常方便地给任意一个战士增加战斗技能,而无需修改原有代码,完全符合开放 – 封闭原则。
2.5 六边形战士
最后展示一下无敌的四皇之一的黑胡子的战斗力:
1 | int main() |
输出的结果如下:
1 | 马歇尔·D·蒂奇依靠惊人的力量和高超的体术战斗... |
关于装饰模式就是在原有基础上一层一层进行包装,对于黑胡子的能力也是如此,不论是Teach 类
还是恶魔果实类的子类DarkFruit、QuakeFruit、PieFruit
它们都是Soldier 类
的子类,所以新的恶魔果实对象是可以为旧的恶魔果实附魔的,因为在恶魔果实内部都绑定了一个实体,他就是黑胡子的对象,最终所有恶魔果实的能力都集中在了这个黑胡子对象身上。
1 | // 给黑胡子附魔 |
3. 结构图
最后根据黑胡子吃恶魔果实的这个例子把装饰模式对应的UML类图画一下(等学会了装饰模式之后,需要先画UML类图再写程序)。
恶魔果实类DevilFruit
就是装饰模式中的装饰类的基类,并且恶魔果实类和父类 Soldier
之间还是聚合关系,通过它的派生类DarkFruit、QuakeFruit、PieFruit
最终实现了对Teach 类
的装饰,使黑胡子这个主体有了三种恶魔果实能力,最终是战力fight()
得到了加成效果。