装饰者模式,见名知意。装饰装饰,何为装饰,无非就是在某个东西上外面再套一层东西,装饰者模式大概就是这样的。
比如现在有一个类A,我们想为它增加一些新的功能,但又不想修改它原来的代码,怎么办?直接的想法是继承,但继承灵活性并不好,推荐多用组合,少用继承。
应用组合:新建一个类B(装饰者),把A(被装饰者)组合到B中,就可以让B行使A的职能,但应该注意两点:
一、A和B应该有相同的超类或接口,这样才能够利用多态在使用A的地方使用B,我们采用继承,让B继承自A。
二、让B继承自A并不是为了继承A类中的方法,而仅仅是声明一个共同的类型,使得在外人看来,A和B是一样的。
上例子:
这是被装饰者
public class Obj { public void doSth(){ System.out.print("i do sth."); } public void doSthElse(){ System.out.print("i do sth else."); } }
现在,我们要扩展它的功能,使得Obj实例在执行doSth()方法时,在后面添加执行时间,而在执行doSthElse()时,在后面添加执行地点。
这是装饰者:
public class DecoraterObj extends Obj { private Obj obj; public DecoraterObj(Obj obj){ this.obj = obj; } public void doSth() { obj.doSth(); System.out.println(" at " + new Date()); } public void doSthElse() { obj.doSthElse(); System.out.println(" at somewhere."); } }
装饰者DecoraterObj继承自被装饰者Obj,故二者拥有相同的对外“接口”,我们本来要使用Obj的地方,现在可以直接使用DecoraterObj了,而且就像在使用Obj一样,因为DecoraterObj中组合了一个Obj引用,它提供了原来Obj的功能,唯一不同的是,我们可以在DecoraterObj中对原Obj进行扩展,可以随意在它之前或之后添加功能。
测试:
public class Main { public static void main(String[] args) { Obj obj = new DecoraterObj(new Obj()); obj.doSth(); obj.doSthElse(); } }
就这样,我们看起来在使用Obj,其实我们已经在它外面又包装了一层。
这个是不是很眼熟?装饰者模式在java I/O中用得非常多,所以我们经常可以看到比如以下代码:
new InputStreamReader(new ButteredInputStream(new InputStream()))
InputStream类提供最简单的二进制读取,外面包装一层BufferedInputStream,提供缓冲功能,然后外面再包装一层InputStremReader,提供以字符方式读取功能。
总结:
装饰者模式的关键在于:统一对外“接口”、多态调用。