proxy, decorator, adpter,三种设计模式之间的差别比较细微,尤其是proxy和decorator,
我尝试说一下自己的理解。
三种模式的差异概况
这三种模式,每一种都可以分为两部分,
- proxy: 原始对象(被代理对象)、代理对象
- decorator: 原始对象(被装饰对象)、装饰对象
- adaptor: 原始对象(被适配对象)、适配对象
每个模式中的原始对象都是一样的,甚至可以是同一个对象,每种模式都会创建一个新的对象,
这个新的对象当然跟原始对象有很大关联,这些关联我将其分为三部分:行为、核心内容、辅助内容。
- 行为:可以理解为接口,或者更通俗一点儿,一个函数代表一种行为
- 内容:可以理解为接口所做的工作,它可能做数值计算并返回计算结果,也可能读写文件,
- 核心内容: 比如对于写操作,数据的write就是核心内容,
- 辅助内容: 而write过程中打印log,发生异常时的出错提示等就属于辅助内容
1 2 3 4 5 6 7 8 9
| +==============================+======+==========+==========+
+==============================+======+==========+==========+
+------------------------------+------+----------+----------+
+------------------------------+------+----------+----------+
+------------------------------+------+----------+----------+
|
- 代理对象: 与原始对象实现同一个接口,所以行为相同,代理对象直接转发原始对象
对应接口的内容,所以核心内容也是一样的,但是代理对象可以在调用原始对象接口的前后
做一些额外的工作,如写日志,增加hook等,想象一下公司的网络代理服务器统计个人流量
就清楚了,这些流量统计并非你要访问的网页的内容(核心内容),而是一下辅助内容。
AOP编程的实现也是利用这个原理。
- 装饰对象: 与原始对象实现同一个接口,所以行为相同,但是装饰对象在调用原始对象
对应接口前后做一些额外工作,但是与代理对象不同的是,这些工作是跟核心内容相关的,
它会增加甚至完全修改原始内容,如给网页增加滚动条,IO stream和writer, reader(bytes
转换为char,这就不只是添加了),当然装饰对象也可以做一些诸如写日志等辅助内容。
- 适配对象:跟原始对象实现两套不同的接口,它完成新、旧两套接口的适配工作,所以
适配对象跟原始对象的行为是不同的,它们的内容显示也应该是不同的,不过内容应该是
很相近的,如三相电转两相电,但是你不能把三相电直接接到自来水管上!
一个示例
通过一个将字符串写入文件的例子来演示。
首先定义接口:
1 2 3 4 5 6 7
| package com.example;
import java.io.File;
public interface StringWriteable { public void write(String content, File file); }
|
下面这个接口用于adaptor做适配,
1 2 3 4 5
| package com.example;
public interface StringWriteableNew { public void write(String content, String filename); }
|
下面是原始类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.example;
import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer;
public class StringWriter implements StringWriteable { @Override public void write(String content, File file) { Writer writer = null; try { writer = new FileWriter(file); writer.write(content); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
|
下面是代理类,它的辅助内容即log打印输出到标准输出,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.example;
import java.io.File;
public class StringWriterProxy implements StringWriteable { private StringWriteable writeable;
public StringWriterProxy(StringWriteable writeable) { this.writeable = writeable; }
@Override public void write(String content, File file) { System.out.println("in proxy: before write."); writeable.write(content, file); System.out.println("in proxy: after write."); } }
|
然后是装饰类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.example;
import java.io.File;
public class StringWriterDecorator implements StringWriteable { private StringWriteable writeable;
public StringWriterDecorator(StringWriteable writeable) { this.writeable = writeable; }
@Override public void write(String content, File file) { writeable.write("this is decorated info...\n" + content, file); } }
|
接着是适配类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.example;
import java.io.File;
public class StringWriterAdaptor implements StringWriteableNew { private StringWriteable writeable;
public StringWriterAdaptor(StringWriteable w) { writeable = w; }
@Override public void write(String content, String filename) { File file = new File(filename); writeable.write(content, file); } }
|
最后是测试类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.example;
import java.io.File;
public class Test { public static void main(String[] args) { String filename = "c:/tmp_file"; String content = "\nThis is the real content...\n"; StringWriteable writeable; StringWriteableNew newWriteable; StringWriteable writer = new StringWriter();
writeable = writer; writeable.write(content, new File(filename));
writeable = new StringWriterProxy(writer); writeable.write(content, new File(filename + "_proxy"));
writeable = new StringWriterDecorator(writer); writeable.write(content, new File(filename + "_decorator"));
newWriteable = new StringWriterAdaptor(writer); newWriteable.write(content, filename + "_adaptor"); } }
|
代理模式的细化
根据代理对象的生成方式,代理模式又分为静态代理和动态代理。
静态代理
静态代理就跟上面的StringWriterProxy
一样,代理类需要手动编写,即静态编写,不再赘述。
动态代理
动态代理就是不用手动编写代理类,代理对象通过反射机制动态生成。代理对象的生成方式一般
有两种:JDK动态代理和cglib动态代理。
JDK动态代理
JDK动态代理根据接口生成代理对象,它要求必须提供接口。
cglib动态代理
cglib动态代理通过继承生成代理对象,因此对于final类,它无能为力。