代理模式(Proxy Pattern)也称为委托模式,属于结构型设计模式,那么何为代理呢?其实生活中很常见,对于我们来说最常接触的莫过于代理上网了,连上代理服务器地址,就可以轻松畅游全世界的网络,还有叫同事帮忙买饭,请律师打官司等等。
定义:
为其他对象提供一种代理以控制对这个对象的访问。
使用场景:
当无法或不想直接访问个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
UML类图:
Subject:抽象主题类,该类的主要职责是专利明真实主题与代理的共同接口方法,该类既可以是一个抽象类也可以是一个接口
RealSubject:真实主题类,该类也称为被委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户类则通过代理类间接地调用真实主题类中定义的方法。
Proxy:代理类,该类也称为委托类或代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用。
示例:
以请律师为例
/**
* 诉讼接口类
* @author Administrator
*
*/
public interface ILawsuit {
//提交申请
void submit();
//进行举证
void burden();
//开始辩护
void defend();
//诉讼完成
void finish();
}
/**
* 具体诉讼人 被代理类
* @author Administrator
*
*/
public class Person 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 Administrator
*
*/
public class Lawyer implements ILawsuit{
private ILawsuit mLawsuit; //持有一个具体被代理者的引用
public Lawyer(ILawsuit lawsuit) {
this.mLawsuit = lawsuit;
}
@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 no1 = new Person();
Lawyer lawyer = new Lawyer(no1);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
代理模式其主要还是一种委托机制,真实对象将方法的执行委托给代理对象,而且委托得干净利落毫不做作,这也是为什么代理模式也称为委托模式的原因。
代理模式大致分为两大部分:
1.静态代理:
代理者的代码由用户自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说在我们的代码运行前代理类的class编译文件就已存在;
2.动态代理:
动态代理则与静态代理相反,通过反射机制动态地生成代理者的对象,也就是说我们在编码阶段压根不需要知道代理谁,代理谁我们将会在执行阶段决定。Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke.
public class DynamicProxy implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
我们主要通过invoke方法来调用具体的被代理方法,动态代理可以使我们的代码逻辑更简洁
/**
* 动态代理类
* @author Administrator
*
*/
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;
}
<span style="font-size:14px;"><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">我们声明一个Object的引用,该引用将指向被代理类,而我们调用被代理类的具体方法则在invoke方法中执行,也就是说我们原来由代理类所做的工作</span>
</span>
现在由InvocationHandler来处理,不再需要关心到底代理谁
public class Client {
public static void main(String[] args) {
ILawsuit no2 = new Person();
//构建一个动态代理
DynamicProxy proxy = new DynamicProxy(no2);
//获取被代理类的ClassLoader
ClassLoader loader = no2.getClass().getClassLoader();
//动态构建一个代理者
ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader, new Class[]{ILawsuit.class},proxy);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
// ILawsuit no1 = new Person();
// Lawyer lawyer = new Lawyer(no1);
// lawyer.submit();
// lawyer.burden();
// lawyer.defend();
// lawyer.finish();
}
}
由此可见动态代理通过一个代理类来代理N多个被代理类,其实质是对代理者与被代理者进行解耦,使两者直接没有直接的耦合关系。相对而言静态代理则只能为给定接口下的实现类伏笔 代理,如果接口不同就需要重新定义不同代理类,较为复杂、但是静态代理更符合面向对象原则。
还可以从适用范围来区分不同类型的代理实现
1.远程代理(Remote Proxy):为某个对象在不同的内存地址空间提供局部代理。使系统可以将Server部分的实现隐藏,以便Client可以不必考虑Server的存在。
2.虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗资源的对象并在真正需要时才创建。
3.保护代理(Protection Proxy):使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同访问权限的情况。
4.智能引用(Smart Reference):在访问原始对象时执行一些自己的附加操作并对指向原始对象的引用计数。
静态和动态代理都可以应用于上面4种情形,两者是各自独立的变化。
总结:
代理模式应用广泛,几乎没有什么缺点可言,它是细分化至很小的一种模式,如果要真的说一个缺点,那么就是所有设计模式的通病:对类的增加。