一、介绍,定义
代理模式也称为委托模式,是一种结构型设计模式,所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。为其他对象提供一种代理以控制对这个对象的访问。
为其它对象提供一种代理以控制对这个对象的访问
二、使用场景
当无法或者不想直接访问某个对象或者访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
三、UML类图
四、通用代码
该类的主要职责是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类也可以是一个接口。
public abstract class Subject {
public abstract void request();
}
RealSubject.java
该类也称为被委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的具体的业务逻辑方法吗,而客户端则通过代理类间接地调用真实主题类中方法。
public class RealSubject extends Subject{
@Override
public void request() {
System.out.println("real subject");
}
}
代理类ProxySubject.java
该类也称为委托类或代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类响应的接口方法执行,以此起到代理的作用。
public class ProxySubject extends Subject {
//持有真实主题的引用
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
//通过真实主题引用的对象调用真实主题中的逻辑方法
realSubject.request();
}
}
客户端Client.java
使用代理类的类型。
public class Client {
public static void main(String[] args) {
//构造一个真实主题
RealSubject realSubject = new RealSubject();
//通过真实主题对象构造一个代理对象
ProxySubject proxySubject = new ProxySubject(realSubject);
//调用代理的相关方法
proxySubject.request();
}
}
角色
Client
客户类,即使用代理类的类型
Subject 抽象主题类 抽象了代理行为
主要职责是声明真实主题与代理的共同接口关系,该类既可以是一个抽象类也可以是一个接口。
RealSubject 实现抽象主题的真实主题类
被委托类或被代理类,定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户端则通过代理类间接地调用真实主题类中定义的方法。
Proxy 委托类或代理类
持有对真实主题类的引用,可以访问真实实体
提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体
控制对实体的存取,并可能负责创建或者删除它
相对与实体的其它附加功能依赖于代理的类型
五、简单实现
小民被老板拖欠工资,小民选择了走发法律途径解决该纠纷。所以小民请律师作为自己的诉讼代理人。
诉讼流程接口类:
public interface ILawSuit {
// 提交申请
void submit();
// 进行举证
void burden();
// 开始辩护
void defend();
// 诉讼完成
void finish();
}
具体诉讼人
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("诉讼成功!判决老板即日起七天内结算工资!");
}
}
小民并不会打官司,于是请代理律师
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 class Client{
public static void main(String[] args) {
ILawSuit xiaoMin=new XiaoMin();
Lawyer lawyer=new Lawyer(xiaoMin);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
静态代理:静态代理如上述代码一样,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说我们的代码运行前代理类的class编译文件就已存在;
动态代理:与静态代理相反,通过反射机制动态地生成代理者的对象,也就是说我们在code阶段不知道要代理谁,代理谁我们将会在执行阶段决定。而Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke.
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;
}
}
如上,我们声明一个Object的引用,指向被代理对象,当我们调用被代理类的具体方法则在invoke方法中执行。很简洁。也就是说我们原来由代理类所做的工作现在由InvocationHandler来处理,不再需要关心到底代理谁。下面修改客户端逻辑:
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();
}
动态代理和静态代理是从code方面来区分代理模式的两种方式,我们也可以从其适用范围来区分不同类型的代理实现。
远程代理(Remote Proxy):为某个对象再不同的内存地址空间提供局部代理。使系统可以将Server部分的实现隐藏,以便Client可以不必考虑Server的存在。
虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗资源的对象并再真正需要时才创建。
保护代理(Protection Proxy):使用代理控制对原始对象的访问。改类型的代理常被用于原始对象有不同访问权限的情况。
智能引用(Smart Reference):在访问原始对象时,执行一些自己的附加操作并对只想原始对象的引用计数。
六、模式的优缺点:
几乎没什么缺点,除了对类的增加。