真诚才是人生最高的品德。 — 乔叟
写在前面
装饰模式在不必改变类文件和使用继承的情况下,可以动态的拓展一个对象的功能,是继承的替代方案之一。它通过创建包装对象也就是装饰者包裹真实的对象。
装饰模式的定义:动态的给一个对象添加一些额外的功能,就增加功能来说,装饰模式比生成子类更加灵活。
抽象组件类(Component):可以是接口或者抽象类,被装饰的最原始的对象。
组件具体实现类(ConcreteComponent):Component的具体实现类,被装饰的具体对象。
抽象装饰类(Decorator):从外类来拓展Component类的功能,但对于Component类来说无需知道Decorator的存在。在它的属性中必然有一个私有变量指向Component抽象组件。
装饰具体实现类(ConcreteDecorator):具体装饰者。
如何装饰
下面通过一个例子来认识装饰模式:
现在有一张画纸,我画了一头奶牛,但是因为我是色盲,所以就让张三来帮忙给画上色,上完色发现这副画真的太好看了,倒不如裱起来挂到墙上欣赏,但是我不会,就让李四来给这幅画装上画框。这里我是被装饰者,张三和李四是装饰者。
1.抽象组件类
我,张三和李四都要操作这幅画,我们都有操作的能力,所以要创建一个抽象类,并声明一个操作的方法。
/**
* 抽象组件类
*/
public abstract class Component {
abstract void todo();
}
2.组件具体实现类
我只有画的能力,上色和安装画框都是在我作画的基础上进行的,我要先作画,因此我是被装饰者。
/**
* 组件具体实现类
*/
public class ConcreteComponent extends Component {
@Override
public void todo() {
Log.d("ConcreteComponent", "我创作了一幅画!!!");
}
}
3.抽象装饰类
抽象装饰类要持有对一个抽象组件的引用,方便调用被装饰对象中的方法,抽象装饰类只是一个声明。
/**
* 抽象装饰类
*/
public abstract class Decoratorextends Component {
private Component mComponent;
public Decorator(Component component) {
mComponent = component;
}
@Override
public void todo() {
mComponent.todo();
}
}
4.装饰具体实现类
当我做好了画,张三和李四就要分别对画进行上色和装框了,张三和李四就是具体的装饰类。
/**
* 装饰具体实现类
*/
public class ZhangSanDecorator extends Decorator {
public ZhangSanDecorator(Component component) {
super(component);
}
@Override
public void todo() {
super.todo();
shangSe();
}
public void shangSe(){
Log.d("张三", "给画上色!!!");
}
}
/**
* 装饰具体实现类
*/
public class LiSiDecorator extends Decorator {
public LiSiDecorator(Component component) {
super(component);
}
@Override
public void todo() {
super.todo();
zhuangKuang();
}
public void zhuangKuang() {
Log.d("李四", "给画装框!!!");
}
}
5.开始装饰
首先我要作画,张三和李四分别对我的画进行装饰,所以要将我作为参数传给张三和李四,这样他们就能在我作画的基础上对画进行装饰了。
/**
* 客户端
*/
public class Client {
public Client() {
// 创建我对象,调用todo()方法作画。
ConcreteComponent me = new ConcreteComponent();
me.todo();
// 创建张三对象,调用todo()方法给画上色。
ZhangSanDecorator zhangSan = new ZhangSanDecorator(me);
zhangSan.todo();
// 创建李四对象,调用todo()方法给画装框
LiSiDecorator liSi = new LiSiDecorator(me);
liSi.todo();
}
}
总结
装饰模式的使用场景:
- 在不影响其他对象的情况下,以动态,透明的方式为单个对象添加职责。
- 需要动态的为一个对象增加功能,这些功能可以动态的撤销。
- 当不能采用继承的方式对系统进行扩充或者采用继承的方式不利于系统扩展和维护时。
装饰模式的优点:
- 通过组合而非继承的方式,动态的扩展对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
- 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
- 具体组件类和具体装饰类可以独立变化,用户可以根据需要增加新的具体组件类和具体装饰类,在使用时再对其进行组合,原有代码无需变化,符合“开发封闭原则”。
装饰模式的缺点:
- 因为所有对象均继承于Component,所以如果Component内部结构发生改变,则不可避免的影响所有子类,就是会影响装饰者和被装饰者。
- 比继承更加灵活机动的特性,也同时意味者装饰模式比继承更加容易出错,排错也很困难。对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。所以只在必要的时候使用装饰模式。
- 装饰层数不能过多,否则会影响效率。