什么是装饰器模式?
按照装饰器模式的定义:能够动态的为一个对象添加一些额外的行为职责的模式。
注意加粗的词:动态。
这里有两层意思:为对象额外添加行为和动态扩展。
什么是动态扩展呢?
答:这个可以与继承进行区别。
我们知道编写一个子类,在其中通过函数覆盖或者函数添加的方式可以扩展子类的行为,但这一扩展是在编译期就确定了,所以说,继承是静态扩展;而装饰器模式则不然,它是在运行期进行扩展的。而且很灵活。在最后我们会总结装饰器模式与继承的几个区别。
为什么要用装饰器模式?
使用装饰器模式而不用继承的原因有以下几个:
1.因为Java中只能使用单根继承,无法支持多继承,所以通过如果想要一个子类实现多个功能的话,必然会产生数量急剧膨胀的子类。
解释:突然想到可以用武侠片中的道理来解释。洪七公会降龙十八掌,欧阳锋会蛤蟆功,这时候单继承就好像你必须有一个师父一样,要么学降龙十八掌,要么学蛤蟆功,不能同时拜欧阳锋和洪七公为师。
但是,郭靖非要两个武功都学怎么办呢?则必须让金庸再创造一个猛人出来,这个猛人两种神功都会,则郭靖只要拜他为师,即可两门功夫全部掌握。(够麻烦的吧)现在如果郭靖想学三种武功呢,四种呢,或者四种中的两种呢?必然会让金庸创造无数个猛人,金老先生得活活累死,而这个江湖得多乱呢。为什么不使用多接口呢?
答:我在刚开始也这样想,但是如果使用一个类实现多个接口,那意思就是有N个接口,每个接口有一个方法,你要想子类拥有N个方法,则必须实现这N个接口,且每个方法,都要自己来实现。
显然这种方法不够灵活,用上面的例子来套,那郭靖也太累了,而且也改变了最初的人物设定!
这个时候如果有一种方式能够让对象只有在用到该方法的时候有该功能,岂不方便?
就像一种作弊器一样,要召唤神龙就召唤神龙,要什么技能就get什么技能,不用的时候就扔掉,岂不轻松?
这就是装饰器模式的好处。
2.还有一种情况,就是如果对象的类是声明为final的,则子类必然无法继承。
师父决定关门不传绝学,你也没有办法。
这个时候只能用装饰器模式了。
装饰器模式的例子
如果上面讲得还没有看懂,请看下面一段我自己写的Demo。
package com.study;
interface Skills {
public void act();
}
class Kungfu implements Skills {
public void act() {
System.out.println("易阳指");
}
}
class Hong extends Kungfu{
Skills skills;
public Hong(Skills skills) {
this.skills = skills;
}
public void act() {
skills.act();
addSkills();
}
public void addSkills() {
System.out.println("九阳神功");
}
}
class Guo implements Skills {
public void act() {
System.out.println("蹲马步");
}
}
public class DecorateDemo {
public static void main(String[] args) {
Skills skills = new Guo();
skills.act();
Skills skill2 = new Hong(skills);
skill2.act();
}
}
运行结果:
蹲马步
蹲马步
九阳神功
装饰器模式与继承的区别
说了这么多,总结一下,继承与装饰器模式的区别:
引用陆舟的一段话:
一方面,我们可以引用到默认的目标实现的行为方式,并加以扩展,看上去就像我们在继承中进行覆盖一样,另一方面这样的设计完全不影响我们在具体装饰实现类中添加新的行为方法职责。
装饰器模式的灵活之处就在于:这样的行为职责扩展方式对于客户端的调用而言是完全透明的。
装饰器比继承更加灵活的应用场景:
1.适合对默认目标实现中的多个接口进行排列组合调度
2.适合对默认目标实现进行选择扩展
3.适合默认目标实现未知或者不易扩展的情况
从Java IOStream源码分析装饰器模式
to be continued...