title: 设计模式:代理模式、装饰器模式和适配器模式之间的差异 date: 2015-05-05 14:15:06 categories: [design pattern]
tags: [design pattern]
proxy, decorator, adpter,三种设计模式之间的差别比较细微,尤其是proxy和decorator, 我尝试说一下自己的理解。
三种模式的差异概况
这三种模式,每一种都可以分为两部分,
- proxy: 原始对象(被代理对象)、代理对象
- decorator: 原始对象(被装饰对象)、装饰对象
- adaptor: 原始对象(被适配对象)、适配对象
每个模式中的原始对象都是一样的,甚至可以是同一个对象,每种模式都会创建一个新的对象, 这个新的对象当然跟原始对象有很大关联,这些关联我将其分为三部分:行为、核心内容、辅助内容。
- 行为:可以理解为接口,或者更通俗一点儿,一个函数代表一种行为
- 内容:可以理解为接口所做的工作,它可能做数值计算并返回计算结果,也可能读写文件,
- 核心内容: 比如对于写操作,数据的write就是核心内容,
- 辅助内容: 而write过程中打印log,发生异常时的出错提示等就属于辅助内容
+==============================+======+==========+==========+
| 模式中涉及的两个对象 | 行为 | 核心内容 | 辅助内容 |
+==============================+======+==========+==========+
| origin and proxy objects | 相同 | 相同 | 不同 |
+------------------------------+------+----------+----------+
| origin and decorated objects | 相同 | 不同 | 不同 |
+------------------------------+------+----------+----------+
| origin and adaptor objects | 不同 | 不同 | 不同 |
+------------------------------+------+----------+----------+
- 代理对象: 与原始对象实现同一个接口,所以行为相同,代理对象直接转发原始对象 对应接口的内容,所以核心内容也是一样的,但是代理对象可以在调用原始对象接口的前后 做一些额外的工作,如写日志,增加hook等,想象一下公司的网络代理服务器统计个人流量 就清楚了,这些流量统计并非你要访问的网页的内容(核心内容),而是一下辅助内容。 AOP编程的实现也是利用这个原理。
- 装饰对象: 与原始对象实现同一个接口,所以行为相同,但是装饰对象在调用原始对象 对应接口前后做一些额外工作,但是与代理对象不同的是,这些工作是跟核心内容相关的, 它会增加甚至完全修改原始内容,如给网页增加滚动条,IO stream和writer, reader(bytes 转换为char,这就不只是添加了),当然装饰对象也可以做一些诸如写日志等辅助内容。
- 适配对象:跟原始对象实现两套不同的接口,它完成新、旧两套接口的适配工作,所以 适配对象跟原始对象的行为是不同的,它们的内容显示也应该是不同的,不过内容应该是 很相近的,如三相电转两相电,但是你不能把三相电直接接到自来水管上!
一个示例
通过一个将字符串写入文件的例子来演示。
首先定义接口:
package com.example;
import java.io.File;
public interface StringWriteable {
public void write(String content, File file);
}
下面这个接口用于adaptor做适配,
package com.example;
public interface StringWriteableNew {
public void write(String content, String filename);
}
下面是原始类,
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打印输出到标准输出,
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.");
}
}
然后是装饰类,
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);
}
}
接着是适配类,
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);
}
}
最后是测试类,
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();
// test writer
writeable = writer;
writeable.write(content, new File(filename));
// test writer proxy
writeable = new StringWriterProxy(writer);
writeable.write(content, new File(filename + "_proxy"));
// test writer decorator
writeable = new StringWriterDecorator(writer);
writeable.write(content, new File(filename + "_decorator"));
// test writer adaptor: StringWriteable -> StringWriteableNew
newWriteable = new StringWriterAdaptor(writer);
newWriteable.write(content, filename + "_adaptor");
}
}
代理模式的细化
根据代理对象的生成方式,代理模式又分为静态代理和动态代理。
静态代理
静态代理就跟上面的StringWriterProxy一样,代理类需要手动编写,即静态编写,不再赘述。
动态代理
动态代理就是不用手动编写代理类,代理对象通过反射机制动态生成。代理对象的生成方式一般 有两种:JDK动态代理和cglib动态代理。
JDK动态代理
JDK动态代理根据接口生成代理对象,它要求必须提供接口。
cglib动态代理
cglib动态代理通过继承生成代理对象,因此对于final类,它无能为力。