美文网首页
设计模式之结构型(7)

设计模式之结构型(7)

作者: 宏势 | 来源:发表于2023-09-10 16:56 被阅读0次

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

    一.适配器模式

    1.一句话描述

    • 修改一个类接口以适用用户期望的另一接口

    2.类图/代码

    image.png

    Callable 目标接口, Runnable 被适配接口, RunnableAdapter适配类, 属于对象适配器

    3.模式分类

    根据适配类和被适配类的关系 分成两种

    • 对象适配模式: 适配类与被适配类是组合关系
    • 类适配模式: 适配类继承被适配类

    4.实战案例

    • Spring工具包的枚举迭代器EnumerationIterator


      image.png
    • SpringMVC的HandlerAdapter


      image.png
    • JDK RunnableAdapter类 把Runnable 适配成Callable

    5.总结

    • 1.接口转换, 转换成用户期望的接口
    • 2.代码复用,可以复用旧类(被适配类)代码
    • 3.便于扩展, 只需增加适配器类就可以使其与现有代码无缝集成

    生活中的插座/转接线 也是非常典型的适配模式,

    二、装饰器模式

    1.一句话描述

    • 在原有的类上增强一个类的功能

    2.类图

    image.png

    FilterInputStream 是装饰器, 以组合方式引用 Inpustream, 增强具体InputStream之类的功能

    3. 实战案例

    • JDK IO输入流设计


      image.png
    • JDK IO输出流设计


      image.png

    4.总结

    1.动态地将功能附加到类上,无需修改原有类代码, 灵活性高
    2.每个具体装饰器类专注于一个特定功能, 吻合单一职责原则
    3.多个装饰器可以按需组合,实现复杂的功能组合

    三、代理模式

    1. 一句话描述

    • 用代理对象来代替真实对象,以控制对真实对象的访问

    2.类图/代码/分类

    代理分为静态代理动态代理

    (1)静态代理

    静态代理在编译时就将接口、实现类、代理类编译成class文件。


    静态代理.png

    (2)动态代理

    动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

    JDK动态代理:

    RealSubject realSubject = new RealSubject();
    Class c = realSubject.getClass();
    Subject subject = (Subject) Proxy.newProxyInstance(c.getClassLoader(), c.getInterfaces(), new DynamicInvocationHandler(realSubject));
     subject.request();
    
    class DynamicInvocationHandler implements InvocationHandler{
        private RealSubject realSubject;
    
        public DynamicInvocationHandler(RealSubject realSubject) {
            this.realSubject = realSubject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("pre ...");
            Object object =  method.invoke(realSubject,args);
            System.out.println("after ....");
            return object;
        }
    }
    

    CGLIB动态代理:

            RealSubject realSubject = new RealSubject();
            Class c = realSubject.getClass();
            Enhancer enhancer = new Enhancer();
            // 设置类加载器
            enhancer.setClassLoader(c.getClassLoader());
            // 设置被代理类
            enhancer.setSuperclass(c);
            // 设置方法拦截器
            enhancer.setCallback(new CGLibMethodInterceptor());
            RealSubject proxy = (RealSubject) enhancer.create();
            proxy.request();
    
    class CGLibMethodInterceptor implements MethodInterceptor{
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("pre ...");
            Object object =  methodProxy.invokeSuper(o,objects);
            System.out.println("after ....1111");
            return object;
        }
    }
    

    3.实战案例

    • Spring AOP 动态代理, 如目标对象实现了接口默认使用JDK动态代理,否则采用CGLIB动态代理

    4.总结

    动态代理有JDK 动态代理、CGLIB 动态代理
    JDK动态代理只能代理实现了接口的类,CGLIB可以代理未实现任何接口的类(继承)
    JDK动态代理直接写Class字节码,CGLIB代理使用ASM框架写Class字节码,相对复杂,生成代理类的效率比较低

    四、外观模式

    1. 一句话描述

    • 提供统一访问入口

    2.类图/代码

    image.png

    3.实战案例

    • SLFJ中的外观模式, 抽象了各种日志框架Logback, log4j, commons-logging, logging
    • spring jdbc中的外观模式 JdbcUtils
    • Tomcat中org.apache.catalina.connector.RequestFacade

    4.总结

    1.简化接口,让接口更简单,引入外观类可以将子系统与客户端解耦
    2.吻合迪米特法则
    3.只需增加一个外观类,客户端只跟外观类交互,由外观类跟多个子系统类交互

    五、桥接模式

    1.一句话描述

    • 把抽象和实现分离,使他们能独立改变

    2.类图

    image.png

    3.实战案例

    4.总结

    1. 解决一个类存在两个独立变化的维度且两个维度都需要进行扩展(吻合开闭原则)
    2. 采用组合方式,更加灵活(吻合 合成/聚合复用原则)

    六、组合模式

    1.一句话描述

    • 可以以一致的方式处理个别对象和对象组合(整体/部分)

    2.类图

    image.png

    3.实战案例

    • 业务系统中菜单就是经典组合模式

    4.总结

    经常跟迭代器模式在一起对比,其实代码结构类似,迭代器模式侧重的遍历这个行为,是行为模式,而组合模式关注重点是结构关系,即整理和部分

    七、享元模式

    1.一句话描述

    • 该模式通过共享技术支持大量细粒度对象复用,以减少内存使用量

    2.类图

    image.png

    3.实战案例

    • Integer、Long、Short、Byte等都是利用了享元模式来缓存-128到127之间的数据
           Integer i1 = 12;
           Integer i2 = new Integer(12);  //无法利用享元
            Integer i3 = Integer.valueOf(12);
            System.out.println(i==i1); //true
            System.out.println(i==i2); //false
            System.out.println(i==i3); //true
    
    • JVM为String类开辟 字符串常量池,存储字符串常量

    • 游戏中的道具、棋子(俄罗斯方块)

    4.总结

    • 当需要创建大量相似对象时,抽取享元类(复用)和非享元类,以减少内存使用

    享元模式VS单例模式,单例模式是为了保证对象全局唯一,享元模式对象复用,节省内存(多例)

    相关文章

      网友评论

          本文标题:设计模式之结构型(7)

          本文链接:https://www.haomeiwen.com/subject/tgmnudtx.html