一.模式定义
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。
Decorator Pattern: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
二.模式要素
Component: 抽象构件
ConcreteComponent: 具体构件
Decorator: 抽象装饰类
ConcreteDecorator: 具体装饰类
三.举例说明
大家应该都玩过王者荣耀,我们就以其中一个很热的英雄孙悟空来举例。
孙悟空有三个技能,一技能护身咒法,二技能斗战冲锋,三技能如意金箍。
如果你只买了英雄而没买皮肤的话,你的猴子是这样的。
如果你买了至尊宝皮肤的话你的猴子是这样的。
如果你买了地狱火皮肤的话你的猴子是这样的。
没有皮肤的猴子完全秀不起来,皮肤除了好看拉风以外,还有个重要的好处就是在局内释放技能的时候的打击效果更加酷炫了。
比如至尊宝的一、二技能都会触发波若波罗密,三技能金箍棒也变成了紫色的大电灯管。
比如地狱火的一、二技能都是一根燃烧的棒子,三技能是一根粗大的火棒子咣的一声插在地上。
想给孙悟空加皮肤、加特效,装饰者模式正好可以用到。先画一下UML图如下:
首先是一个IHero接口,包括待实现的展示一、二、三技能的方法。
接着SunWukong作为一个英雄实现IHero这个接口,展示属于自己的三个技能。
接着有了皮肤系统后,Skin抽象类也先实现IHero接口,因为对不同皮肤来说,三个技能的效果也是不一样的。
最后具体的皮肤类,ZhiZunbao、HellFire继承自Skin,在类中以构造器的方式传入英雄。在三个展示技能的方法中额外加上各自皮肤的特效。
四.代码实现
IHero.java
package decoratePattern;
/**
* @program: Test
* @description: 英雄接口
* @author: Lei Dong
* @create: 2019-04-21 12:10
**/
public interface IHero {
/**
* 展示一技能
*/
void showSkill1();
/**
* 展示二技能
*/
void showSkill2();
/**
* 展示三技能
*/
void showSkill3();
}
SunWukong.java
package decoratePattern;
/**
* @program: Test
* @description: 孙悟空
* @author: Lei Dong
* @create: 2019-04-21 12:09
**/
public class SunWukong implements IHero {
@Override
public void showSkill1() {
System.out.println("护身咒法");
}
@Override
public void showSkill2() {
System.out.println("斗战冲锋");
}
@Override
public void showSkill3() {
System.out.println("如意金箍");
}
}
Skin.java
package decoratePattern;
/**
* @program: Test
* @description: Skin
* @author: Lei Dong
* @create: 2019-04-21 12:15
**/
public abstract class Skin implements IHero {
@Override
public abstract void showSkill1();
@Override
public abstract void showSkill2();
@Override
public abstract void showSkill3();
}
ZhiZunbao.java
package decoratePattern;
/**
* @program: Test
* @description: 至尊宝
* @author: Lei Dong
* @create: 2019-04-21 12:18
**/
public class ZhiZunbao extends Skin {
private IHero hero;
public ZhiZunbao(IHero hero) {
this.hero = hero;
}
@Override
public void showSkill1() {
System.out.print("至尊宝皮肤1技能特效 ");
hero.showSkill1();
}
@Override
public void showSkill2() {
System.out.print("至尊宝皮肤2技能特效 ");
hero.showSkill2();
}
@Override
public void showSkill3() {
System.out.print("至尊宝皮肤3技能特效 ");
hero.showSkill3();
}
}
HellFire.java
package decoratePattern;
/**
* @program: Test
* @description: 至尊宝
* @author: Lei Dong
* @create: 2019-04-21 12:18
**/
public class HellFire extends Skin {
private IHero hero;
public HellFire(IHero hero) {
this.hero = hero;
}
@Override
public void showSkill1() {
System.out.print("地狱火皮肤1技能特效 ");
hero.showSkill1();
}
@Override
public void showSkill2() {
System.out.print("地狱火皮肤2技能特效 ");
hero.showSkill2();
}
@Override
public void showSkill3() {
System.out.print("地狱火皮肤3技能特效 ");
hero.showSkill3();
}
}
Main.java
package decoratePattern;
/**
* @program: Test
* @description: Main
* @author: Lei Dong
* @create: 2019-04-21 12:04
**/
public class Main {
public static void main(String[] args) {
IHero hero = new SunWukong();
hero.showSkill1();
hero.showSkill2();
hero.showSkill3();
System.out.println("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~");
ZhiZunbao zhiZunbao = new ZhiZunbao(hero);
zhiZunbao.showSkill1();
zhiZunbao.showSkill2();
zhiZunbao.showSkill3();
System.out.println("~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~");
HellFire hellFire = new HellFire(hero);
hellFire.showSkill1();
hellFire.showSkill2();
hellFire.showSkill3();
}
}
运行结果:
五.总结
1.装饰者模式的优点
(1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
(2)可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
(3)通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
(4)具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
2.装饰者模式的缺点
(1)使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
(2)这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。