借用一本关于设计模式上的介绍来说说装饰器模式:
初看上图感觉装饰器模式有点像俄罗斯套娃,而装饰器模式的核心就是在不改变原有类的基础上给类新增功能。不改变原有类,让大家想起了继承、AOP切面,当然这些方式都可以实现,但是使用装饰器模式会是另外一种更为灵活的思路,可以避免继承导致的子类过多,也可以避免AOP带来的复杂性。
java中常用的装饰器模式应用场景
new BufferedReader(new FileReader("")); 这段代码你是否熟悉,相信学习java开发到字节流、字符流、文件流、的内容时都见到了这样的代码,一层嵌套一层,一层嵌套一层,字节流转字符流等等,而这种方式的使用就是装饰器模式的一种体现。
刚开始看装饰者模式的时候还是挺正常的,然而看了两遍越看越觉得不对,特别是网上官方的那些解释。
例如:菜鸟教程
此处定义了一个图形接口,然后是两个实现类实现了图形接口。
然后创建了一个抽象装饰类和一个实体装饰类。
然后是用用装饰器实体类去装饰被装饰类,实现被装饰类功能的添加。
但是我很不理解,为什么呢?因为我一开始的想法是装饰器是固定的的一个,然后当你像将被装饰者对象进行装饰的时候只用将被装饰者加入装饰器即可,可是菜鸟教程的意思是,每个装饰器实体类它只能完成它特定的装饰功能,例如颜色装饰器它就只能对图形对象装饰颜色。但多个装饰器可以实现多层装饰。所以我觉得菜鸟教程上的这个例子并不太好。
下面我还是用博客园上面的例子比较好。
首先定义一个抽象人的接口:
package com.user;
/**
* 定义接口
* @author T
*
*/
public interface AbstractPerson {
//具有展示的功能
void show() ;
}
然后定义一个什么都没穿的人的实体类(这个实体类继承自抽象接口人类)
package com.user;
/**
* 定义一个具体的人,就是被装饰者
* @author T
*
*/
public class Me implements AbstractPerson {
@Override
public void show() {
System.out.println( "什么都没穿,我展示的是裸体");
}
}
然后再定义一个抽象装饰器类(它也实现了抽象人类接口)
package com.user;
/**
* 定义抽象装饰物
* @author T
*
*/
public abstract class AbstractClothes implements AbstractPerson {
AbstractPerson abstractPerson ;
public AbstractClothes( AbstractPerson abstractPerson ){
this.abstractPerson = abstractPerson ;
}
@Override
public void show() {
abstractPerson.show();
}
}
然后再定义装饰器实体类。一个帽子装饰器(专门用来装饰帽子的装饰器),继承自抽象装饰器类:
package com.user;
/**
* 帽子装饰物
* @author T
*
*/
public class Hat extends AbstractClothes {
public Hat(AbstractPerson abstractPerson) {
super(abstractPerson);
}
@Override
public void show() {
super.show();
say();
}
public void say(){
System.out.println( "我展示一个帽子");
}
}
再定义一个鞋子装饰器类(专门用来装饰鞋子):
package com.user;
/**
* 鞋子装饰物
* @author T
*
*/
public class Shoes extends AbstractClothes {
public Shoes(AbstractPerson abstractPerson) {
super(abstractPerson);
}
@Override
public void show() {
super.show();
say();
}
public void say(){
System.out.println( "我展示一双鞋子");
}
}
然后创建一个测试类进行测试:
package com.user;
public class Test {
public static void main(String[] args) {
//创建被装饰者
Me me = new Me() ;
//裸体的人被装饰了帽子 ,具有了展示帽子的能力
Hat hat = new Hat( me ) ;
// 带了帽子的人被装饰了鞋子,具有了展示鞋子的本领
Shoes shoes = new Shoes( hat ) ;
shoes.show();
}
}
这个例子就很直白了,首先创建一个人的对象,然后用帽子装饰器类对人这个对象进行装饰,使人有戴帽子的功能。然后再用鞋子装饰器类对戴帽子的人对象进行装饰,使戴帽子的人穿上鞋子。这样理解起来就舒服多了。
下面是代理模式(静态代理)
定义一个代理戴帽子代理类。
public class HadProxy implements AbstractPerson {
private final AbstractPerson person;
public CoffeeProxy() {
this.person = new Me();
}
@Override
public void printMaterial() {
System.out.println("我展示一个帽子");
this.person.show();
}
}
装饰器模式和代理模式的区别
对装饰器模式来说,装饰者(Decorator)和被装饰者(Decoratee)都实现一个接口。对代理模式来说,代理类(Proxy Class)和真实处理的类(Real Class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。
在上面的例子中,装饰器模式是使用的调用者从外部传入的被装饰对象(coffee),调用者只想要你把他给你的对象装饰(加强)一下。而代理模式使用的是代理对象在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。调用者不知道你找了其他人,他也不关心这些事,只要你把事情做对了即可。
装饰器模式关注于在一个对象上动态地添加方法,而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例;当使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰器的构造器。
装饰器模式和代理模式的使用场景不一样,比如IO流使用的是装饰者模式,可以层层增加功能。而代理模式则一般是用于增加特殊的功能,有些动态代理不支持多层嵌套。
代理和装饰其实从另一个角度更容易去理解两个模式的区别:代理更多的是强调对对象的访问控制,比如说,访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆,简单地说就是将某个控制访问权限应用到多个对象上;而装饰器更多的强调给对象加强功能,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象上。
所以,代理模式注重的是对对象的某一功能的流程把控和辅助,它可以控制对象做某些事,重心是为了借用对象的功能完成某一流程,而非对象功能如何。而装饰模式注重的是对对象功能的扩展,不关心外界如何调用,只注重对对象功能加强,装饰后还是对象本身。