代理模式 (设计模式)

几天开始学习另外一种类型的设计模式: 结构型模式。

结构型模式,主要包括,门面,代码,适配,装饰,桥接,享元模式。 

在设计模式中,结构型模式和行为型模式是两个不同的概念,它们分别关注于软件设计中的不同方面。

    1. 结构型模式(Structural Patterns):

      • 结构型模式关注的是类与对象之间的组合,通过使用继承和组合来形成更大的结构,从而解决软件设计中的对象组织和关系问题。
      • 这些模式主要用于处理类或对象的组合,以便在运行时实现新的功能或适应不同的场景。
      • 典型的结构型模式包括:适配器模式、装饰器模式、代理模式、组合模式、桥接模式、享元模式。
    2. 行为型模式(Behavioral Patterns):

      • 行为型模式关注的是对象之间的通信和职责分配,它们解决的是对象之间的协作问题,以实现更加灵活和可扩展的设计。
      • 这些模式主要用于描述对象之间的交互、职责分配和算法的使用。
      • 典型的行为型模式包括:模板方法模式、策略模式、命令模式、责任链模式、状态模式、观察者模式、中介者模式、访问者模式、备忘录模式、解释器模式。

代理模式: 

代理模式的原理和代码实现都不难掌握。它在不改变原始类(或者被代理类)代码的情况下,通过引入代理类来给原始类附加功能。我们通过例子来解释一下这段话。 

比如我们在前边中讲到的性能技术器。 当我们开发了一个MetricsController 类,用来收集接口请求的原始数据,比如访问时间,处理市场等,在业务系统中,我们采用如下方式使用这个类

public class UserController {
    //...省略其他属性和方法...
    private MetricsCollector metricsCollector; // 依赖注入
    public UserVo login(String telephone, String password) {
        long startTimestamp = System.currentTimeMillis();
// ... 省略login逻辑...
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimestamp;
        RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimes
                metricsCollector.recordRequest(requestInfo);
//...返回UserVo数据...
    }
    public UserVo register(String telephone, String password) {
        long startTimestamp = System.currentTimeMillis();
// ... 省略register逻辑...
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimestamp;
        RequestInfo requestInfo = new RequestInfo("register", responseTime, startTi
                metricsCollector.recordRequest(requestInfo);
//...返回UserVo数据...
    }
}

  

很明显上边代码有两个问题,第一,性能计数器框架代码侵入到业务代码中,跟业务代码高度耦合。如果未来需要替换这个框架,那替换的成本比较大。第二,手机接口请求的代码跟业务代码无关,本就不应该放到一个类中。业务类最好职责更加单一。

为了将框架代码和业务代码解耦,代理模式就排上了用场。代理类UserControllerProxy 和原始UserConterooler 实现了相同的接口IUserControler .UserController 赋值业务的功能,代理类UserControlerProxy 负责业务代码中执行其他附加逻辑,并通过委托的方式调用原始类来执行业务代码。 具体的代码实现如下所示: 

public interface IUserController {
    UserVo login(String telephone, String password);

    UserVo register(String telephone, String password);
}

public class UserController implements IUserController {
    //...省略其他属性和方法...
    @Override
    public UserVo login(String telephone, String password) {
//...省略login逻辑...
//...返回UserVo数据...
    }

    @Override
    public UserVo register(String telephone, String password) {
//...省略register逻辑...
//...返回UserVo数据...
    }
}

public class UserControllerProxy implements IUserController {
    private MetricsCollector metricsCollector;
    private UserController userController;

    public UserControllerProxy(UserController userController) {
        this.userController = userController;
        this.metricsCollector = new MetricsCollector();
        @Override
        public UserVo login (String telephone, String password){
            long startTimestamp = System.currentTimeMillis();
// 委托
            UserVo userVo = userController.login(telephone, password);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimes
                    metricsCollector.recordRequest(requestInfo);
            return userVo;
        }
        @Override
        public UserVo register (String telephone, String password){
            long startTimestamp = System.currentTimeMillis();
            UserVo userVo = userController.register(telephone, password);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimestamp;
            RequestInfo requestInfo = new RequestInfo("register", responseTime, startTi
                    metricsCollector.recordRequest(requestInfo);
            return userVo;
        }
    }

    //UserControllerProxy使用举例
//因为原始类和代理类实现相同的接口,是基于接口而非实现编程
//将UserController类对象替换为UserControllerProxy类对象,不需要改动太多代码
    IUserController userController = new UserControllerProxy(new UserController()
}

  

参照基于接口而非实现的编程设计思想,将原始类对象替换为代理类对象的时候,为了让代码改动尽量少,在刚刚的代理模式的代码实现中,代理类和原始类都实现相同的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的(它来自一个第三方库) 我们也没有办法,之际修改原始类,给它重新定义一个接口,在这种情况下,我们该如何实现代理模式呢? 

对于这种外部类的扩展,我们一般都是采用继承的方式,这也不例外。我们让代理类继承原始类,然后扩展附加功能。原理很简单。 

动态代理的原理解析

不过,刚刚的代码实现,还是有点问题,一方面,我们需要在代理类中,将原始类中的所有的方法,都重新实现一遍,并且为每个方法都附加相似的代码逻辑。另一方面,如果要天际的附加功能的类不止一个。我们需要针对每个类都创建一个代理类。

如果有50 个类都要附加功能呢的原始类,那我们就要创建50 个对应的代理类。者会导致项目中类的个数成本增加,增加了代码维护成本,并且,每个代理类中的代码都有点像模板的样子重复,也增加了不必要的开发成本。那么这个问题如何解决。 

我们可以用动态代理来解决这个问题,所谓动态代理,就是我们不需要事先为么个原始类编写代理类,而是在运行的时候,动态的创建原始类的代理类,然后在系统中代理类替换掉原始类,那如何实现动态代理?

如果你熟悉的是java 语言,那么动态代理实现是一件非常简单的事情,因为java 语言本身已经提供了动态代理的语法,(实际上,动态代理地城依赖的就是java de 反射语法)下边是动态代理的例子

public class MetricsCollectorProxy {
    private MetricsCollector metricsCollector;

    public MetricsCollectorProxy() {
        this.metricsCollector = new MetricsCollector();
    }

    public Object createProxy(Object proxiedObject) {
        Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
        DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
        return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), in
    }

    private class DynamicProxyHandler implements InvocationHandler {
        private Object proxiedObject;

        public DynamicProxyHandler(Object proxiedObject) {
            this.proxiedObject = proxiedObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Th

        long startTimestamp = System.currentTimeMillis();
        Object result = method.invoke(proxiedObject, args);
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimestamp;
        String apiName = proxiedObject.getClass().getName() + ":" + method.getNam
        RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTim
                metricsCollector.recordRequest(requestInfo);
return result;
    }
}

  

实际上apo 底层的实现原理就是基于动态代理。 用户配置好需要给那些类创建代理,并定义好执行原始的业务代码前后执行那些附加功能,Spring 为这类创建动态代理。 Spring 为这些类创建动态代理,并在Jvm 中替原始的对象,原本在代码中执行的原始类的方法,被换做执行代理类的方法,也就是实现给原始类添加附加功能的目的。 

代理模式的应用场景: 

代理模式最常用的一个应用场景就是,在业务系统中开发一些非功能性的需求,比如将恐,统计,健全,限流,事务,幂等,日志,

实际上rpc ,缓存中的应用。 RPC 它也可以成为一种代码,我们把这种代理称之为远程代理。 

 

posted @ 2024-03-01 11:06  dousil  阅读(1)  评论(0编辑  收藏  举报