设计模式-代理模式、外观模式

代理模式

定义

  • 为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或不能直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 当一个复杂对象的多份副本存在时,代理模式可以结合享元模式以减少存储器用量。典型做法是创建一个复杂对象及多个代理对象,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会传送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

作用

  1. 保护目标对象
  2. 增强目标对象

类图

角色

  • 抽象角色(Subject): 通过接口或抽象类声明真实角色实现的业务方法
  • 代理角色(Proxy): 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
  • 真实角色(Real Subject): 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用

优缺点

  • 优点:
    • 职责清晰
    • 高拓展性
    • 智能化
    • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
    • 代理对象可以扩展目标对象的功能;
    • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
  • 缺点:
    • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
    • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂

使用案例

  1. 引用计数指针对象
  2. Windows 里面的快捷方式
  3. spring aop

动态代理

基于JDK 的动态代理的实现步骤

  1. 先创建代理类构造器(InvocationHandler接口的实现类),重写接口中invoke方法
    其中method.invoke(被代理的对象,参数),来调用需要调用的方法。我们可以在此方法上增强原方法的功能

  2. 可以将代理类建造方法Proxy.newProxyInstance 写在代理类构造器中,封装为getProxy方法,返回Object 类型代理对象,使用时强转类型,这时参数为(任意对象.class.getClassLoader,接口(两种写法,一种是直接写接口.class,一种是实现类.class.getInterfaces()),代理类构造器(this)

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    public class ProxyInvocationHandler implements InvocationHandler {
    /**
    * 被代理接口
    */
    private Object target;

    复制代码
     public ProxyInvocationHandler(Object target) {
         this.target = target;
     }
    
     public Object getProxy(){
         return Proxy.newProxyInstance(
                 target.getClass().getClassLoader(),
                 target.getClass().getInterfaces(),
                 this);
     }
    
    
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         // todo 这部分可以处理代理对象的逻辑
         System.out.println("代理对象调用之前");
         Object res = method.invoke(target, args);
         System.out.println("代理对象调用之后");
         return res;
     }

    }

    public static void main(String[] args) {
    // 被代理对象
    MapReal mapReal = new MapReal();
    // 代理对象
    ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(mapReal);
    // 创建代理对象
    MapInterface mapproxy = (MapInterface) proxyInvocationHandler.getProxy();
    // 调用代理对象的方法
    mapproxy.display();
    }

基于 CGLIB 实现的动态代理

复制代码
public class CglibTest implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理类 调用前");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("cglib代理类 调用后");
        return result;
    }

    public Object getProxy(Class cla) {
        return net.sf.cglib.proxy.Enhancer.create(cla, this);
    }
}

public static void main(String[] args) {
        CglibTest cglibTest = new CglibTest();
        MapReal mapReal = (MapReal) cglibTest.getProxy(MapReal.class);
        mapReal.display();
    }

外观模式

定义

  • 外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合
  • 外观模式的实现核心主要是-由外观模式去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法
  • 适配器模式是将一个对象包装起来以改变其接口,而外观模式是将一群对象包装起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口

类图

角色

  • 门面(Facade)角色:客户端调用这个角色的方法。该角色知道相关的一个或多个子系统的功能和责任,该角色会将从客户端发来的请求委派到相应的子系统中去。
  • 子系统(subsystem)角色:可以同时包含一个或多个子系统。每个子系统都不是一个单独的类,而是一个类的集合。每个子系统都可以被客户端直接调用或被门面角色调用。对于子系统而言,门面仅仅是另外一个客户端,子系统并不知道门面的存在。

优缺点

  • 优点
    • 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。
    • 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可
    • 一个子系统的修改对其他子系统没有任何影响,而且子系统的内部变化也不会影响到外观对象
  • 缺点
    • 外观模式不能很好的限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性
    • 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则

适用场景

  • 当要为访问一系列复杂的子系统提供一个简单统一的入口时可以使用外观模式
  • 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而是通过外观类建立联系,降低层之间的耦合度
相关推荐
触底反弹41 分钟前
🤯 面试被问 AI Workflow 和 Agent 有啥区别?3 张图 + 2 段代码讲清楚!
人工智能·设计模式·面试
杨充21 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充21 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充21 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充21 小时前
2.面向对象的特性
设计模式
杨充21 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充21 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充1 天前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法