参考
- 《设计模式:可复用面向对象软件的基础 》4.7 Proxy 代理--对象结构型模式
- 《Android源码设计模式解析与实战》第18章 编程好帮手--代理模式
意图
为其它对象提供一种代理以控制对这个对象的访问
适用性
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。
- 远程代理 为一个对象在不同的地址空间提供局部代表。这一点Android Binde实现跨进程通信很典型
- 虚代理 根据需要创建开销很大的对象。
- 保护代理 控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
- 智能指引 取代了简单的指针,它在访问对象时执行一些附加操作。
- 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它,难道Java就是这个原理?
- 当第一次引用一个持久对象时,将它装入内存
- 在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
结构
角色
- Client
客户类,即使用代理类的类型
- Subject 抽象主题类 抽象了代理行为
主要职责是声明真实主题与代理的共同接口关系,该类既可以是一个抽象类也可以是一个接口。
- RealSubject 实现抽象主题的真实主题类
被委托类或被代理类,定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户端则通过代理类间接地调用真实主题类中定义的方法。
- Proxy 委托类或代理类
- 持有对真实主题类的引用,可以访问真实实体
- 提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体
- 控制对实体的存取,并可能负责创建或者删除它
- 相对与实体的其它附加功能依赖于代理的类型
协作
代理根据其种类,在适当的时候向RealSubject转发请求
效果
Proxy模式在访问对象时引入了一定程度的间接性。根据代理的类型,附件的间接性有多种用途
- Remote Proxy可以隐藏一个对象存在不同地址空间的事实。
- Virtual Proxy 可以进行优化,例如根据要求创建对象。
- Protection Proxies和Smart Reference都允许在访问一个对象时有一些附加的内务处理
应用例子1 小明打官司
描述
小明被老板拖欠工资,小明选择了走发法律途径解决该纠纷。所以小明请律师作为自己的诉讼代理人。
结构
java实现
/**
*
* @author newtrekwang
* @desc 代理接口
* 2017年3月4日
*/
public interface ILawSuit {
// 提交申请
void submit();
// 进行举证
void burden();
// 开始辩护
void defend();
// 诉讼完成
void finish();
}
/**
*
* @author newtrekwang
* @desc 被代理者小明
* 2017年3月4日
*/
public class XiaoMin implements ILawSuit{
@Override
public void submit() {
//小明申请仲裁
System.out.println("老板拖欠工资,特此申请仲裁!");
}
@Override
public void burden() {
System.out.println("这是合同书和过去一年的银行工资流水");
}
@Override
public void defend() {
System.out.println("证据确凿,不需要再说什么了");
}
@Override
public void finish() {
System.out.println("诉讼成功!判决老板即日起七天内结算工资!");
}
}
/**
*
* @author newtrekwang
* @desc 代理律师
* 2017年3月4日
*/
public class Lawyer implements ILawSuit {
// 持有一个具体被代理者的引用
private ILawSuit mLawSuit;
public Lawyer(ILawSuit iLawSuit) {
this.mLawSuit=iLawSuit;
}
@Override
public void submit() {
mLawSuit.submit();
}
@Override
public void burden() {
mLawSuit.burden();
}
@Override
public void defend() {
mLawSuit.defend();
}
@Override
public void finish() {
mLawSuit.finish();
}
}
客户使用
public static void main(String[] args) {
ILawSuit xiaoMin=new XiaoMin();
Lawyer lawyer=new Lawyer(xiaoMin);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
结果
老板拖欠工资,特此申请仲裁!
这是合同书和过去一年的银行工资流水
证据确凿,不需要再说什么了
诉讼成功!判决老板即日起七天内结算工资!
java的静态代理和动态代理
- 静态代理:静态代理如上述代码一样,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说我们的代码运行前代理类的class编译文件就已存在;
- 动态代理:与静态代理相反,通过反射机制动态地生成代理者的对象,也就是说我们在code阶段不知道要代理谁,代理谁我们将会在执行阶段决定。而Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler{
private Object obj;//被代理的类引用
public DynamicProxy(Object obj) {
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用被代理类对象的方法
Object result=method.invoke(obj, args);
return result;
}
}
使用
public static void main(String[] args) {
// 构造一个小明
ILawSuit xiaoMin=new XiaoMin();
// 构造一个动态代理
DynamicProxy dynamicProxy=new DynamicProxy(xiaoMin);
// 获取被代理类小明的ClassLoader
ClassLoader loader=xiaoMin.getClass().getClassLoader();
// 动态构造一个代理者律师
ILawSuit lawyer=(ILawSuit) Proxy.newProxyInstance(loader, new Class[]{ILawSuit.class}, dynamicProxy);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
结果
老板拖欠工资,特此申请仲裁!
这是合同书和过去一年的银行工资流水
证据确凿,不需要再说什么了
诉讼成功!判决老板即日起七天内结算工资!
应用例子2 嵌入图形对象的文档编辑器
描述
对一个对象进行访问控制的一个原因时为了只有在我们真正需要这个对象时才对它进行创建和初始化。考虑一个可以在文档中嵌入图形对象的文档编辑器,有些图形对象(大图)的创建开销很大,但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象,因为并非所有这些对象在文档中都同时可见,所以也没有必要同时创建这些对象。
这一限制条件意味着,对于每一个开销很大的对象,应该根据需要进行创建。问题的解决方案就是使用另一个对象,即图像Proxy,代替那个真正的图像。Proxy可以代替一个图像对象,并且在需要时负责实例化这个图像对象。
结构
源码就略了
应用例子3 Retrofit 中的动态代理
待更
应用例子4 Android Service代理
待更