概述
在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。
在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。
定义:
动态地给一个对象添加一些额外的职责,就添加功能来说,装饰模式比生成子类更为灵活。
举个栗子
杨过本身就会全真剑法,有两位武学前辈洪七公和欧阳锋分别向杨过传授过打狗棒法和蛤蟆功,这样杨过除了会全真剑法,还会打狗棒法和蛤蟆功。洪七公和欧阳锋就起到了“装饰”杨过的作用。
抽象组件
定义一个武侠的抽象类,以及使用武功的方法
public abstract class Swordsman {
//Swordsman 武侠有使用武功的抽象方法
public abstract void attackMagic();
}
组件具体实现类
被修饰的具体对象
public class YangGuo extends Swordsman {
@Override
public void attackMagic() {
//杨过初始的武学是全真剑法
System.out.println("全真剑法");
}
}
抽象装饰者
抽象装饰者保持了一个对抽象组件的引用,方便调用被装饰对象中的方法。在这个例子中就是武学前辈要持有武侠的引用,方便教授他武学并使他融会贯通:
public class Master extends Swordsman {
private Swordsman mSwordsman;
public Master(Swordsman swordsman){
this.mSwordsman = swordsman;
}
@Override
public void attackMagic() {
mSwordsman.attackMagic();
}
}
装饰者具体实现类
这个例子中用两个装饰者具体实现类,分别是洪七公和欧阳锋,它们负责向杨过传授新的武功
public class HongQiGong extends Master {
public HongQiGong(Swordsman swordsman) {
super(swordsman);
}
public void teachAttackMagic(){
System.out.println("洪七公教授打钩棒法");
System.out.println("杨过使用打狗棒法");
}
public void attackMagic(){
super.attackMagic();
teachAttackMagic();
}
}
public class OuYangFeng extends Master {
public OuYangFeng(Swordsman swordsman) {
super(swordsman);
}
public void teachAttackMagic(){
System.out.println("欧阳锋教授蛤蟆功");
System.out.println("杨过使用打蛤蟆功");
}
public void attackMagic(){
super.attackMagic();
teachAttackMagic();
}
}
客户端调用
经过洪七公和欧阳锋的指导,杨过除了初始武学全真剑法又学会了打狗棒法和蛤蟆功。
public class Client {
public static void main(String[] args) {
//创建杨过
YangGuo mYangGuo = new YangGuo();
//洪七公向杨过传授打狗棒法,杨过学会了打狗棒法
HongQiGong mHongQiGong = new HongQiGong(mYangGuo);
mHongQiGong.teachAttackMagic();
//欧阳锋向杨过传授蛤蟆功,杨过学会了蛤蟆功
OuYangFeng mOuYangFeng = new OuYangFeng(mYangGuo);
mOuYangFeng.teachAttackMagic();
}
}
装饰模式的使用场景和优缺点
- 使用场景
- 在不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责
- 需要动态地给一个对象增加功能,这些功能可以动态地撤销。
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
- 优点
- 通过组合而非继承的方式,动态地扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
- 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
- 具体组件类与具体装饰类可以独立变化,用户可以根据需要添加新的具体组件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开放封闭原则”。
- 缺点
- 因为所有对象均继承于Component,所以如果Component内部结构发生改变,则不可避免的影响所有子类(装饰者和被装饰者)。如果基类改变,则势必影响对象的内部。
- 比继承更加灵活机动的特性,也同时意味着装饰者模式比继承更加易于出错,较为繁琐。所以,只在必要的时候使用装饰者模式。
- 装饰层数不能过多,否则会影响效率。
借鉴刘望舒《Android进阶之光》