解锁设计模式的力量:使用代理模式简化重构
代理模式是一种常见的设计模式,它可以为其他对象提供一个代理以控制对这个对象的访问。这种模式是一种结构型模式,它的主要目的是通过引入一个代理对象来间接访问一个对象,从而可以在访问对象时增加一些额外的处理逻辑。
什么是代理模式
GoF(Gang of Four,四人组)在《设计模式:可复用面向对象软件的基础》一书中给出的代理模式定义:
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理模式的英文为 Proxy,它是一种对象结构型模式。
在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外服务。
代理模式的定义说明了它的主要作用:为另一个对象提供一个代理以控制对该对象的访问。同时,代理模式也强调了代理对象和目标对象之间的接口必须是相同的,这保证了客户端可以通过代理对象来访问目标对象。在代理模式中,代理对象承担了控制访问、保护目标对象、增加额外服务等职责,这样可以使得目标对象更加纯粹,只需要关注自己本身的业务逻辑即可。
组成成分
代理模式包含三个主要组成成分,分别是:
- 抽象主题角色(Subject):定义了代理角色和真实主题角色的共同接口,代理角色通过该接口调用真实主题角色的方法。
- 真实主题角色(Real Subject):实现了抽象主题角色定义的接口,代表真实的业务对象。
- 代理角色(Proxy):实现了抽象主题角色定义的接口,并持有真实主题角色的引用,代表了真实主题角色的代理。在代理角色中,可以添加额外的功能,如记录日志、权限控制等,而这些功能并不是真实主题角色本身具备的功能。
除了以上三个组成成分,代理模式还可能包含一些其他的辅助类或对象,用于实现具体的功能。例如,代理角色可能需要在运行时创建真实主题角色的实例,也可能需要对真实主题角色的方法调用进行拦截和处理等。因此,在实现代理模式时,还需要根据具体的需求设计和实现相应的辅助类或对象。
代码实现
一个生活中的例子可以是使用代理购买电影票。
在某些情况下,你可能需要购买电影票,但你不想亲自去电影院排队购票。这时,你可以选择使用代理购买电影票。
代理可以是电影院的官方网站或第三方电影票代理商。你可以通过这些代理购买电影票,并且代理会为你处理所有的购票事务,包括选座、付款和领取电影票等。你只需要在代理网站上输入电影、时间和地点等信息,并进行支付,就可以获得电影票。
在这个例子中,代理模式为你提供了一个便捷的方式来购买电影票,你无需亲自前往电影院排队,可以通过代理完成购票流程。同时,代理还提供了额外的服务,如在线选座和支付等功能,这些服务可以提高购票的体验和效率。因此,代理模式在生活中也有着广泛的应用。
// 抽象主题角色
public interface Subject {
void request();
}
// 真实主题角色
public class RealSubject implements Subject {
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理角色
public class Proxy implements Subject {
private RealSubject realSubject;
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("Proxy: Preparing request.");
}
private void postRequest() {
System.out.println("Proxy: Request finished.");
}
}
// 客户端角色
public class Client {
public static void main(String[] args) {
Subject subject = new Proxy();
subject.request();
}
}
动态代理
我们有一个接口Subject
,有一个实现类RealSubject
,我们要使用 JDK 动态代理来生成一个代理对象proxy
,代理对象的调用会转发给RealSubject
对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public interface Subject {
void request();
}
public class RealSubject implements Subject {
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
public class DynamicProxy implements InvocationHandler {
private Object realObject;
public DynamicProxy(Object realObject) {
this.realObject = realObject;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用方法前进行一些额外的操作
System.out.println("DynamicProxy: Logging before the request.");
// 调用实际对象的方法
Object result = method.invoke(realObject, args);
// 在调用方法后进行一些额外的操作
System.out.println("DynamicProxy: Logging after the request.");
return result;
}
}
public class Client {
public static void main(String[] args) {
// 创建被代理对象
RealSubject realSubject = new RealSubject();
// 创建代理对象
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new DynamicProxy(realSubject)
);
// 调用代理对象的方法
proxy.request();
}
}
在上述代码中,RealSubject
表示真实的业务实现类,DynamicProxy
表示动态代理类,Client
表示客户端。我们通过创建RealSubject
对象,然后创建一个代理对象proxy
来访问RealSubject
。
在代理对象中,我们实现了InvocationHandler
接口,并重写了其中的invoke()
方法,在调用代理对象的方法时,invoke()
方法会被自动调用。在invoke()
方法中,我们可以进行一些额外的操作,比如在调用实际对象的方法前后添加日志等。
使用Proxy.newProxyInstance()
方法来创建代理对象。该方法需要传入三个参数:ClassLoader
对象,Class
对象数组和InvocationHandler
对象。其中,ClassLoader
对象用于加载代理类,Class
对象数组表示代理类需要实现的接口列表,InvocationHandler
对象用于处理代理对象的方法调用。
优缺点
代理模式的优点:
- 代理模式可以隐藏真实对象的实现细节,客户端只需要与代理对象进行交互,无需直接与真实对象进行交互,降低了系统的耦合度。
- 代理模式可以提高系统的安全性,代理对象可以控制客户端对真实对象的访问权限。
- 代理模式可以实现远程访问,通过代理对象可以访问远程主机上的对象,扩展了系统的功能。
- 代理模式可以提高系统的性能,通过代理对象可以缓存真实对象的结果,避免频繁访问真实对象。
代理模式的缺点:
- 代理模式会增加系统的复杂度,需要额外的代码实现代理对象。
- 代理模式会降低系统的效率,因为需要在代理对象和真实对象之间进行数据传递,会增加系统的开销。
- 代理模式对于一些特定的场景可能不太适用,比如对于频繁创建的对象,使用代理模式可能会带来更多的负担。
转载自:https://juejin.cn/post/7228007210747559997