目录
代理模式定义
为对象提供一种代理以控制对这个对象的访问。
优点
-
控制访问:代理对象可以控制客户端对目标对象的访问,可以加入额外的逻辑,如权限校验、日志记录等。
-
实现延迟加载:代理对象可以在需要时才创建和初始化具体对象,实现延迟加载,提高性能。
-
解耦:代理模式可以将客户端与具体主题解耦,客户端只关心抽象主题接口。
缺点
-
增加复杂性:引入代理对象会增加代码复杂性,特别是涉及多个代理或者复杂代理逻辑的情况下。
-
性能开销:在频繁创建代理对象或者执行代理逻辑的场景下,可能会引入性能开销。
代理模式结构说明
-
抽象主题(Subject):定义目标对象和代理对象的共同接口,客户端通过该接口与目标对象或代理对象交互。
-
具体主题(ConcreteSubject):目标对象,是代理对象所代表的真正对象,负责执行实际的操作。
-
代理(Proxy):代理对象,实现了抽象主题接口,包含了对目标对象的引用。代理对象通常控制这对目标对象的引用,并可以在访问前后执行一些额外的操作。
工作流程
-
客户端通过抽象主题接口与代理对象进行交互。
-
代理对象收到客户端的请求,可以在请求前后执行一些操作,然后将请求转发给具体主题对象(目标对象)。
-
具体主题执行请求的实际操作。
-
结果返回给代理对象,代理对象将结果放回给客户端。
代码示例
a.静态代理:儿子工作忙,父亲代理儿子相亲找对象
1.创建抽象主题Person,提供抽象方法找对象findLove()。
public interface Person { void findLove(); }
2.目标对象Son,实现Person接口。
public class Son implements Person { public void findLove(){ System.out.println("相亲要求:肤白貌美大长腿,想想就好..."); } }
3.代理对象Father,实现Person接口,包含目标对象的引用。
public class Father implements Person { private Son son; public Father(Son son){ this.son = son; } public void findLove(){ System.out.println("老爸帮儿子相亲..."); son.findLove(); System.out.println("没找到"); } }
4.客户端代码,老爸代理儿子相亲
public class ProxyClient { public static void main(String[] args){ Father father = new Father(new Son()); // 静态代理只能是指定的类型,难于扩展 father.findLove(); } }
运行结果:
b.动态代理(通常使用Java的反射机制来实现):专业的人干专业的事,媒婆登场。
1.创建抽象主题Person,提供抽象方法找对象findLove。
public interface Person { void findLove(); }
2.创建目标对象Customer,就是被代理对象。
public class Customer implements Person { public void findLove(){ System.out.println("男方身高1米8四肢发达,找肤白貌美大长腿..."); } }
3.创建动态代理对象MatchMaker,需要实现InvocationHandler接口。
public class MatchMaker implements InvocationHandler { private Object target; // 通过反射获取目标对象 public Object getInstance(Object target) { this.target = target; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } // invoke方法增强对象 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("媒婆给你找对象,你放心..."); Object object = method.invoke(this.target, args); System.out.println("觉得怎么样?可以就定了..."); return object; } }
4.客户端动态代理,不需要关注被代理者类型
public class DynamicClient { public static void main(String[] args) { try { Person p = (Person)new MatchMaker().getInstance(new Customer()); p.findLove(); } catch (Exception e){ System.out.println("异常信息:"); e.printStackTrace(); } } }
结果:
应用场景
-
远程代理:用于实现远程对象的访问,例如通过网络调用远程服务。
-
虚拟代理:用于实现延迟加载,只有在需要时才创建和初始化具体对象。
-
保护代理:用于控制对目标对象的访问权限,进行权限验证或访问控制。
-
缓存代理:用于在请求对象的结果缓存中,减少对目标对象的频繁访问。
-
日志记录:用于记录对象的操作历史,实现日志记录功能。
本质
代理模式的本质是控制访问,它引入了一个中间层来管理客户端与目标对象之间的交互。
涉及的设计原则
-
单一职责原则(Single Responsibility Principle):代理模式有助于实现单一职责原则,代理对象可以负责额外的功能,而具体主题只需关注核心功能。
相关的设计模式
-
装饰者模式(Decorator Pattern):装饰者模式允许动态地添加额外的行为,而代理模式通常控制对目标对象的访问。它们都可以用于增强对象的功能。
-
适配器模式(Adapter Pattern):适配器模式也可以包装一个对象,但其主要目的是为了适配不同的接口,而代理模式的目的是控制对目标对象的访问。
开源框架中的引用
据库连接池中使用代理模式。代理对象(数据库连接代理)可以在获取数据库连接时执行一些额外的操作,如连接池管理、日志记录等,然后将请求转发给具体的数据库连接对象。这样,代理可以控制数据库连接的访问并提供附加功能,而客户端只需与代理交互。常见的数据库连接池库(如Apache DBCP、HikariCP等)使用了代理模式。