一 、模式定义
代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
Proxy Pattern:Provide a surrogate or placeholder for another object to control access to it.
二、 模式中包含的角色
(1)IRent(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
(2)HouseProxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供了一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
(3)Host(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
三、 模式类图
四、 模式实现:模拟中介租房
1、静态代理
优点:
- 可以降低类与类之间的耦合。
- 可以不修改原有类的基础上进行功能的扩展。
缺点:
- 结构比较复杂。
- 一个代理角色只能代理一个真实角色。
(1)抽象类(IRent):
package proxy_01;
//抽象角色
public interface IRent {
public void rent();
}
(2)代理类(HouseProxy):
package proxy_01;
//代理类---中介
public class HouseProxy implements IRent{
private Host host;
public HouseProxy(Host host) {
this.host=host;
}
public void rent() {
seeHouse();
host.rent();
contract();
money();
}
private void seeHouse() {
System.out.println("中介带领看房子");
}
private void contract() {
System.out.println("中介签合同");
}
private void money() {
System.out.println("中介收款");
}
}
(3)真实类(Host):
package proxy_01;
//真实类---房东
public class Host implements IRent{
public void rent() {
System.out.println("房东出租房子");
}
}
(4)客户端(Client):
package proxy_01;
//客户端---测试
public class Client {
public static void main(String[] args) {
//真实对象
Host host = new Host();
host.rent();
//代理对象
HouseProxy hp = new HouseProxy(host);
//通过代理对象来调用,无需再和真实对象打交道
hp.rent();
}
}
2、动态代理
动态代理:不需要提前创建好相应的代理类,而是在程序运行过程中动态的去创建
实现方式主要包含两大类:基于接口、基于类
- 基于接口:原生JDK — Proxy、InvocationHandle
- 基于类:cglib
- 利用字节码实现:javassist
(1)抽象类(IRent):
package proxy_02;
//抽象角色
public interface IRent {
public void rent();
}
(2)代理类(ProxyInvocationHandle):
package proxy_02;
//代理类---中介
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandle implements InvocationHandler{
private Host host;
public void setRent(Host host) {
this.host=host;
}
//提供一个方法,让外界获取代理对象
public Object getProxy() {
//newProxyInstance返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
/*
* 参数:loader - 定义代理类的类加载器
* interfaces - 代理类要实现的接口列表
* h - 指派方法调用的调用处理程序
* */
Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), host.getClass().getInterfaces(), this);
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object result = method.invoke(host, args);
money();
return result;
}
public void seeHouse() {
System.out.println("中介带领看房子");
}
public void money() {
System.out.println("中介收费");
}
}
(3)真实类(Host):
package proxy_02;
//真实类---房东
public class Host implements IRent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
(4)客户端(Client):
package proxy_02;
//客户端---测试
public class Client {
public static void main(String[] args) {
/*
* 动态代理的实现:我们没有写代理类,是通过反射来创建的代理对象
**/
//真实角色
Host host = new Host();
//动态代理角色 --- InvocationHandle是代理角色的处理程序
//通过调用代理角色处理程序来处理要调用的接口对象
ProxyInvocationHandle pih = new ProxyInvocationHandle();
//告诉动态代理对象,它是在给哪个接口代言
//因为host对象实现李IRent接口
pih.setRent(host);
//获取动态代理对象
IRent proxy = (IRent) pih.getProxy();
proxy.rent();
}
}