设计模式还有哪些你不知道的事儿?

什么是代理模式?

代理模式是很常见的一种设计模式,其过程是为其他对象提供一种代理以控制对特定对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

在现实生活中,代理随处可见,各类中介就类似于代理,例如:房产中介是卖房子的代理,旅游中介是旅游提供商的代理;携程、美团、淘宝都可以称为之卖方的代理;遇到法律纠纷,可以聘请一名专业人士作为代理律师;办理证件如果不熟悉流程,可以找代办人员等等。

代理模式应用哪些方面?

在软件设计中,使用代理的意图也很多,例如:为了安全原因,需要屏蔽客户端直接访问真实的对象;在远程调用过程中,需要使用代理处理远程方法调用的技术细节;为了提升系统性能,对真实对象进行封装,实现延迟加载的目的。

不同类型的代理也具有独特的特点,例如:

远程代理:位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率,常用到的Nginx、Apache Server 都属于此类代理。

虚拟代理:当需要一个消耗资源较少的对象来代表一个资源消耗较多的对象,从而降低系统开销、缩短运行时间可以用此类型的代理。

缓冲代理:当需要为某一个频繁访问的操作结果提供一个临时空间,以供多个客户端共享访问这些结果时可以应用此类型代理。通过缓冲代理,提升访问的效率,提高系统吞吐量。

保护代理:软件设计中,权限管理时不可缺失的部分。当需要对某一个对象或者资源进行控制时,可以应用此代理。

此外,还有JDK中的动态代理类、cglib、Spring Aop、日志的采集、Spring的事务管理、全局捕获异常、分布式事务原理代理数据源等等。代理模式的应用能够协调调用者和被调用者,在一定程度上降低了系统的耦合度,客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。当然代理的存在肯定也会带来额外的性能开销和程序复杂程度的增加,这也是在系统设计时需要权衡的点。

代理模式的实现示例

接下来,以Java语言为例,我们看看代理模式中典型的两种类型的实现方式

  1. 静态代理
/** * 描述: 代理接口 * * @author tokin.deng * @date 2021/11/24 **/public interface Work {    void execute();}
/** * 描述: 代理实现类 * * @author tokin.deng * @date 2021/11/24 **/public class RealWork implements Work {    public void execute() {        System.out.println("正在进行编码工作...");    }}
/** * 描述: 静态代理类 * * @author tokin.deng * @date 2021/11/24 **/public class StaticProxy implements Work {    private RealWork work;    public StaticProxy(RealWork work) {        this.work = work;    }    public void execute() {        System.out.println("工作前,先吃早餐.....");        work.execute();        System.out.println("工作后,要吃晚餐.....");    }}
/** * 描述: 静态打工人 * * @author tokin * @date 2021/11/24 **/public class StaticPeople {    public static void main(String[] args) {        // 创建一个代理实例        StaticProxy proxy = new StaticProxy(new RealWork());        // 执行工作任务        proxy.execute();    }}

从源码上可以看出,静态代理的缺陷很明显,在使用时必须事先知道真实对象的存在,并将其作为代理对象的内部属性;一个真实对象对应一个代理对象,如果真实对象很多,那么代理对象的大量使用将是一个灾难;此外,如果事先不知道真实对象是谁,又该怎么使用代理呢? 下面的动态代理就解决了这个问题。

2)动态代理:

Jdk 提供了InvocationHandler接口和Proxy类,以这两个工具实现动态代理。

/** * 描述: 代理接口 * * @author tokin.deng * @date 2021/11/24 **/public interface Work {    void execute();}
/** * 描述: 代理实现类 * * @author tokin.deng * @date 2021/11/24 **/public class RealWork implements Work {    public void execute() {        System.out.println("正在进行编码工作...");    }}
/** * 描述: 动态代理工厂 * * @author tokin.deng * @date 2021/11/24 **/public class DynamicProxyFactory {    // 维护一个目标对象    private Object target;    public DynamicProxyFactory(Object target) {        this.target = target;    }    //给目标对象生成代理对象    public Object getProxyInstance() {        return Proxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(),                new InvocationHandler() {                    @Override                    public Object invoke(Object proxy, Method method, Object[] args)                        throws Throwable {                        //方法调用之前                        System.out.println("工作前,先吃早餐" + method);                        //运用反射执行目标对象方法                        Object returnValue = method.invoke(target, args);                        //方法调用之后                        System.out.println("工作后,要吃晚餐" + method);                        return returnValue;                    }                }        );    }}
/** * 描述: 动态打工人 * * @author tokin.deng * @date 2021/11/24 **/public class DynamicPeople {    public static void main(String[] args) {        // 确定需要代理的对象        RealWork realWork = new RealWork();        // 动态代理工厂类,生产代理        Work work = (Work) new DynamicProxyFactory(realWork).getProxyInstance();        // 干活        work.execute();    }}

源码中可以看出,动态代理不需要在代理工厂中指定具体接口和实现类,根据实际的业务动态的产生对象。

这就是我们SpringBean工厂的基础实现,有兴趣的小伙伴欢迎讨论。

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年10月20日
下一篇 2021年10月20日

相关推荐