结构型模式之代理模式
一、概念和使用场景
1、概念
代理模式是一种结构型设计模式,它允许我们为其他对象提供一个代理或者占位符,以控制对该对象的访问。
2、核心思想
代理模式的核心思想是在访问一个对象时引入一个代理对象,通过代理对象来控制访问的权限和行为。代理对象和被代理对象具有相同的接口,这样客户端就可以通过代理对象来访问被代理对象。代理对象可以拦截对被代理对象的访问,并添加额外的逻辑,例如权限检查、缓存等。
3、java实现代理模式的方式
在Java中实现代理模式有两种方式:静态代理和动态代理。
4、使用场景
Java代理模式的使用场景有以下几个:
-
远程代理:当对象存在不同地址空间时,可以使用远程代理来隐藏对象的具体实现细节,使得客户端可以像访问本地对象一样访问远程对象。远程代理主要用于网络通信中的远程方法调用。
-
虚拟代理:当创建一个对象需要很长时间时(例如加载大量数据或者进行复杂的计算),可以使用虚拟代理来延迟对象的创建。虚拟代理在真正需要使用对象时才会创建,可以节省系统资源。
-
安全代理:当需要控制对某个对象的访问权限时,可以使用安全代理来限制对对象的直接访问。安全代理可以在访问对象之前进行权限验证,确保只有具有相应权限的用户才能访问对象。
-
缓存代理:当需要缓存对象的结果时,可以使用缓存代理来提高系统的性能。缓存代理会在调用对象的方法之前检查缓存,如果缓存中已经存在相同的结果,则直接返回缓存中的结果,而不去真正执行方法。
-
日志记录代理:当需要记录对象的操作日志时,可以使用日志记录代理来实现。日志记录代理会在调用对象的方法之前进行日志记录,可以记录下对象的操作信息,方便后续的审计和分析。
总之,代理模式可以在不改变原有对象的情况下,通过引入代理对象来增加一些额外的功能,提供更好的控制和封装。
二、示例讲解
1. 静态代理
静态代理是在编译时就已经确定代理对象的类型,代理对象和被代理对象都需要实现同一个接口。代理对象在调用被代理对象的方法前后可以执行一些额外的逻辑。
下面是一个简单的静态代理的示例代码:
定义一个接口 SaleHouse
//待出售房产
public interface SaleHouse {
//房产信息
Object houseInformation(String price);
}
被代理的角色(房子主人) HouseOwner
package org.storemanage.regestercenter.proxymode;
/**
* @CreateTime: 2024-08-30
* @Description: 房屋主人
* @Version: 1.0
* @Author: hkf
*/
public class HouseOwner implements SaleHouse {
private final String name ="阳光别墅区";
private final String address ="四川省成都市武侯区双楠大道xxx小区123号";
private String price;
@Override
public Object houseInformation(String price) {
String houseInfo ="200 W";
if (!price.isEmpty()) {
houseInfo= "房产名称:"+name+"\n 地址为:"+address+"\n房产价格为:"+price;
}
return houseInfo ;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "HouseOwner{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", price='" + price + '\'' +
'}';
}
}
代理角色 (房屋中介) HouseAgent
package org.storemanage.regestercenter.proxymode;
/**
* @CreateTime: 2024-08-30
* @Description: 房屋中介
* @Version: 1.0
* @Author: hkf
*/
public class HouseAgent implements SaleHouse {
private String price;
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
HouseOwner houseOwner;
//中介为了赚佣金,提高了出售的价格
@Override
public Object houseInformation(String price) {
Object houseInfo ="";
if (houseOwner == null) {
houseOwner = new HouseOwner();
houseInfo = houseOwner.houseInformation(price);
}
return houseInfo;
}
@Override
public String toString() {
return "HouseAgent{" +
"price='" + price + '\'' +
", houseOwner=" + houseOwner +
'}';
}
}
客户端调用者 ClientHouseAgent
package org.storemanage.regestercenter.proxymode;
/**
* @CreateTime: 2024-08-30
* @Description: 客户端代理实现
* @Version: 1.0
* @Author: hkf
*/
public class ClientHouseAgent {
public static void main(String[] args) {
HouseAgent houseAgent = new HouseAgent();
//中介可以自定义价格,赚取佣金
Object houseInfo = houseAgent.houseInformation("236 w");
System.out.println(houseInfo);
}
}
结果如图
2. 动态代理
动态代理是在运行时通过反射机制动态生成代理对象,代理对象不需要实现接口,但需要指定一个接口或者父类来实现代理。
下面是一个简单的动态代理的示例代码:
定义接口 SaleHouse
package org.storemanage.regestercenter.proxymode;
//待出售房产
public interface SaleHouse {
//房产信息
Object houseInformation(String price);
}
定义被代理角色 HouseOwner
package org.storemanage.regestercenter.proxymode;
/**
* @CreateTime: 2024-08-30
* @Description: 房屋主人
* @Version: 1.0
* @Author: hkf
*/
public class HouseOwner implements SaleHouse {
private final String name ="阳光别墅区";
private final String address ="四川省成都市武侯区双楠大道xxx小区123号";
private String price;
@Override
public Object houseInformation(String price) {
String houseInfo ="200 W";
if (!price.isEmpty()) {
houseInfo= "房产名称:"+name+"\n 地址为:"+address+"\n房产价格为:"+price;
}
return houseInfo ;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "HouseOwner{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", price='" + price + '\'' +
'}';
}
}
创建代理角色 HouseAgentInvocationHandler
package org.storemanage.regestercenter.proxymode;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @CreateTime: 2024-08-30
* @Description: 中介动态代理
* @Version: 1.0
* @Author: hkf
*/
public class HouseAgentInvocationHandler implements InvocationHandler {
private Object target;
public HouseAgentInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object invoke = method.invoke(target, args);
System.out.println(" After method: " + method.getName());
return invoke;
}
}
客户端代码 ClientHouseDemo
package org.storemanage.regestercenter.proxymode;
import java.lang.reflect.Proxy;
/**
* @CreateTime: 2024-08-30
* @Description: 动态代理客户端代码
* @Version: 1.0
* @Author: hkf
*/
public class ClientHouseDemo {
public static void main(String[] args) {
HouseOwner houseOwner = new HouseOwner();
HouseAgentInvocationHandler handler = new HouseAgentInvocationHandler(houseOwner);
SaleHouse saleHouse=(SaleHouse)Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(), houseOwner.getClass().getInterfaces(),handler
);
Object o1 = saleHouse.houseInformation("443 W");
System.out.println(o1);
}
}
结果如图
在上述代码中,我们首先定义了一个接口 SaleHouse
,然后实现了该接口的具体类 HouseOwner
。接着,定义了 HouseAgentInvocationHandler
类作为动态代理的 InvocationHandler
,在 invoke
方法中可以添加额外的逻辑。在客户端代码中,我们创建了 HouseOwner
对象和 HouseAgentInvocationHandler
对象,然后利用 Proxy
类的 newProxyInstance
方法生成代理对象,最后调用代理对象的 houseInformation
方法。
三、总结
1、使用规则
无论是静态代理还是动态代理,代理模式都可以在不改变被代理对象的情况下,对访问进行控制和添加额外的逻辑。代理模式可以提高代码的可维护性和可扩展性,但也会增加代码的复杂性和性能开销,因此在使用代理模式时需要根据具体的情况进行权衡。
2、代理模式的优点包括:
-
代理模式可以实现真实对象的延迟加载。当真实对象的创建和初始化过程比较耗时时,使用代理模式可以在需要的时候才创建真实对象,提高系统的性能和响应速度。
-
代理模式可以提供额外的安全性控制。通过代理对象来控制对真实对象的访问,可以在访问前后进行一些额外的安全性检查和操作,增加系统的安全性。
-
代理模式可以实现对真实对象的访问控制和管理。通过代理对象可以控制对真实对象的访问和调用,可以实现一些额外的管理功能,如权限控制、缓存管理等。
3、代理模式的缺点包括:
-
代理模式增加了系统的复杂度。引入代理对象会增加系统中的类和对象的数量,增加系统的复杂度和维护成本。
-
代理模式可能会引入性能问题。由于代理模式引入了额外的层次,可能会在一定程度上降低系统的性能,特别是在频繁访问真实对象的情况下。
-
代理模式的设计需要考虑到代理对象和真实对象之间的接口的一致性,使得系统的设计和实现更加复杂。