小Y:Hello,大家好,欢迎来到魂斗罗.归来的世界,下面让小Y带领大家一起去采访一下叼烟大汉比尔·雷泽,让大家更加理解这个粗狂的战斗汉子。Let's go。!
小Y:你最喜欢干什么?
比尔·雷泽:最喜欢冲关打爆大机。
小Y:比尔,你想对观众说些什么?
比尔·雷泽:想挑战我,随时奉陪!一颗不够,给你来三颗!
小Y:......
比尔·雷泽作为魂斗罗这么经典的人物,原来也是一个粗狂耿直boy呀。为了保存住他的光辉形象和让大家更加了解他,小Y决定把比尔·雷泽的攻击技能装饰一番介绍给大家认识。
一、案例设想
比尔·雷泽在战场上在战场上基本上用散弹枪可以横扫小兵,但是遇到一些特殊的情况还是需要通过G5手雷、集速手雷、爆破飞弹等技能来进行辅助。那么如何设置这些技能才合适呢?简单,小Y脑海中立马闪过几种方案:
(1)采用继承的方式,设定一个比尔·雷泽类,有多少个技能就写多少个子类来继承这个比尔·雷泽类。
看完这个图,小Y自己都蒙逼了,每增加一个技能可以组合出N个子类,这只是一个比尔·雷泽,再来个艾薇、寒锋什么的,那不就要加到天荒地老?
(2)直接抽象一个角色类,里面包含了每个角色的技能。
这样子有多少个角色就增加多少个子类就可以了,不用根据技能增加类,避免造成子类爆炸,但是,每个角色的技能是可以叠加使用的,角色越多或者技能叠加种类越多,那么就要在超类增加越多的方法,而且直接修改超类不符合开闭原则(超类对扩展开放,对修改关闭)。
(3)今天的主题— 装饰模式。
二、装饰模式的概念
1.装饰模式定义
装饰模式(Decorator Pattern)是一种比较常见的模式,动态地给一个对象添加一些额外的职责(就增加功能来说,装饰模式相比生成子类更为灵活)。
2.使用的场景
- 需要扩展一个类的功能,或者给一个类添加附加职责(即核心功能不变,对其进行扩展)。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实,使得子类数目呈爆炸性增长。
3.角色介绍
-
Component抽象构件
Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象(在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件) 。
-
ConcreteState具体状态角色
ConcreteComponent 具体构件,是Component的具体实现,要装饰的就是它 。
-
Decorator装饰角色
一般是一个抽象类,实现Component接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件(如果具体装饰角色只有一个,这个可以省略)。
-
ConcreteDecorator具体装饰角色
把你最核心的、最原始的、最基本的东西装饰成想要的东西。
4.案例实现
(1)装饰模式实现角色装饰UML图
①抽象角色类
public abstract class Character {
public abstract void attack();
}
②实现角色比尔·雷泽(对它进行装饰)
public class BillRizer extends Character {
@Override
public void attack() {
//角色最基本的技能
System.out.print("散弹枪进行扫射!");
}
}
③技能装饰抽象类
public abstract class SkillDecorator extends Character{
private Character character;
public SkillDecorator(Character character) {
this.character = character;
}
@Override
public void attack() {
this.character.attack();
}
}
④G5手雷技能
public class G5GrenadeDecorator extends SkillDecorator {
public G5GrenadeDecorator(Character character) {
super(character);
}
@Override
public void attack() {
super.attack();
System.out.print("G5手雷辅助攻击!");
}
}
⑤集速手雷技能
public class SetspeedDecorator extends SkillDecorator {
public SetspeedDecorator(Character character) {
super(character);
}
@Override
public void attack() {
super.attack();
System.out.print("集速手雷辅助!");
}
}
⑥爆破飞弹技能
public class ExplosiveMissileDecorator extends SkillDecorator {
public ExplosiveMissileDecorator(Character character) {
super(character);
}
@Override
public void attack() {
super.attack();
System.out.print("爆破飞弹辅助!");
}
}
⑦客户端
public class Client {
public static void main(String[] args){
//需要装饰的BillRizer
BillRizer billRizer=new BillRizer();
//使用G5手雷辅助
G5GrenadeDecorator g5GrenadeDecorator=new G5GrenadeDecorator(billRizer);
g5GrenadeDecorator.attack();
//使用G5手雷+集速手雷+爆破飞弹辅助
SetspeedDecorator setspeedDecorator=new SetspeedDecorator(g5GrenadeDecorator);
ExplosiveMissileDecorator explosiveMissileDecorator=new ExplosiveMissileDecorator(setspeedDecorator);
explosiveMissileDecorator.attack();
}
}
输出结果:
①散弹枪进行扫射!G5手雷辅助攻击!
②散弹枪进行扫射!G5手雷辅助攻击!集速手雷辅助!爆破飞弹辅助!
把比尔·雷泽的技能介绍一番,大家会发现原来这个看似粗狂的大汉真的霸气十足啊,有资格看你不爽就来一句“想挑战我,随时奉陪!一颗不够,给你来三颗”。
三、总结分析
优点:
①装饰类和被装饰类可以独立发展,而不会相互耦合。Component类无须知道Decorator类,而Decorator也不用知道具体的构件,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
②装饰模式是继承关系的一个替代方案。装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
③通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。缺点:
①这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
②使用装饰模式进行系统设计时将产生很多小对象,这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。