• 学点设计模式,盘点Spring等源码中与设计模式的那些事之结构型模型


    学点设计模式,盘点Spring等源码中与设计模式的那些事之结构型模型

    首先,我们先要了解结构型设计模式都包括哪些:适配器模式(Adapter)、桥接模式(Bridge)、过滤器模式(Filter)、组合模式(Composite)、装饰器模式(Decorator/Wrapper)、外观模式(Facade)、享元模式(Flyweight)、代理模式(Proxy)

    结构型模型主要关注怎样组合对象或者类,所以结构型模式又分为类结构模式(类的组合,继承)和对象结构模式(类与对象的组合,关联关系)。又因为合成复用原则告诉我们,要尽量使用关联关系代替继承关系,所以结构模型大多数都是对象结构模型。

    1、适配器模式

    • 定义:将一个接口转换成用户希望的另一个接口,使接口不兼容的类可以一起工作
    • 举例:日本人想要观看咱们中国的电影,我们不直接去修改电影播放器让其出现双语字母,而是新建一个适配器,让外国人使用适配器播放,适配器带有语言转换的能力。
      在这里插入图片描述
      对于这个场景,既可以使用类结构(继承)也可以使用对象结构(组合),代码没啥差别,下边采用对象结构实现
    //播放器接口
    public interface Player {
        String play();
    }
    //翻译器接口
    public interface Translator {
        String translate(String content);
    }
    //播放器实现
    public class MoviePlayer implements Player {
        @Override
        public String play() {
            System.out.println("正在播放:java从入门到精通");
            String content = "你好";
            System.out.println(content);
            return content;
        }
    }
    //转换器实现
    public class Zh_JPTranslator implements Translator{
        @Override
        public String translate(String content) {
            if("你好".equals(content)){
                return "空尼几哇";
            }
            if ("什么".equals(content)){
                return "纳尼";
            }
            return "*******";
        }
    }
    //播放器适配器
    public class JPMoviePlayerAdapter implements Player {
        //组合的方式
        private Translator translator = new Zh_JPTranslator();
        private Player target;//被适配对象
        public JPMoviePlayerAdapter(Player target){
            this.target = target;
        }
    
        @Override
        public String play() {
            String play = target.play();
            //转换字幕
            String translate = translator.translate(play);
            System.out.println("日文:"+translate);
            return play;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    在这里插入图片描述

    • 核心:系统中所有原有的东西都不能修改,扩展一个新的类,来连接两个之前不同的类

    2、桥接模式

    • 定义:将抽象与实现解耦,使两者都可以独立变化(真正引起类变化的维度抽取出来),桥接将继承转为关联,降低类之间的耦合度,减少代码量。

    • 举个栗子:有两种手机,拍照手机和性能手机,两种手机在线上和线下的价格都不同,考虑这个类里边的一些东西会扩展很多,就应该把这个东西分离出来。
      在这里插入图片描述

    • 代码示例:

    /**
     * 1、抽象手机类
     *  手机有各种销售渠道价格都不一样
     */
    public abstract class AbstractPhone {
        //【真正会引起此类变化的一个维度直接抽取出来,通过组合的方式接起来】
        AbstractSale sale; //分离渠道【桥接的关注点】
        
        //当前手机的描述
        abstract String getPhone();
    
        public void setSale(AbstractSale sale) {
            this.sale = sale;
        }
    }
    
    /**
     * 抽象销售渠道
     */
    public abstract class AbstractSale {
    
        private String type;
        private Integer price;
        public AbstractSale(String type,Integer price){
            this.type = type;
            this.price = price;
        }
    
        String getSaleInfo(){
            return "渠道:"+type+"==>"+"价格:"+price;
        }
    }
    
    //哪种手机,根据手机种类实现......多个
    public class PhotoPhone extends AbstractPhone{
        @Override
        String getPhone() {
            return "PhotoPhone:"+sale.getSaleInfo();
        }
    }
    
    /**
     * 线上渠道,根据渠道实现......多个
     */
    public class OnlineSale extends AbstractSale{
        public OnlineSale(String type, Integer price) {
            super(type, price);
        }
    }
    
    //TestClass
    public class MainTest {
        public static void main(String[] args) {
            //拍照手机的线上渠道
            PhotoPhone iPhone = new PhotoPhone();
            iPhone.setSale(new OnlineSale("线上",2599));
    
            String phone = iPhone.getPhone();
            System.out.println(phone);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    3、装饰器模式

    • 定义:创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能
    • 对比适配器模式:适配器是连接两个类,可以增强一个类;装饰器是只增强一个类不用连接两个类。
      附一个网上很常见的例子
    • 代码示例
    //附一个网上很常见的例子,给图像增加颜色
    //抽象的图像
    public interface Shape {
       void draw();
    }
    
    //具体的图像:圆......可以多个,这里只示例一个
    public class Circle implements Shape {
       @Override
       public void draw() {
          System.out.println("Shape: Circle");
       }
    }
    
    //抽象的装饰类
    public abstract class ShapeDecorator implements Shape {
       protected Shape decoratedShape;
     
       public ShapeDecorator(Shape decoratedShape){
          this.decoratedShape = decoratedShape;
       }
       public void draw(){
          decoratedShape.draw();
       }  
    }
    
    //具体的装饰类
    public class RedShapeDecorator extends ShapeDecorator {
     
       public RedShapeDecorator(Shape decoratedShape) {
          super(decoratedShape);     
       }
       @Override
       public void draw() {
          decoratedShape.draw();         
          setRedBorder(decoratedShape);
       }
       private void setRedBorder(Shape decoratedShape){
          System.out.println("Border Color: Red");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    4、代理模式

    • 定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用

    • 代理模式又分为静态代理和动态代理,静态代理和装饰器十分相似,所以大家经常认为装饰器是代理的一种。

    下面来看一下动态代理

    • jdk动态代理示例
      在这里插入图片描述
    //一个抖音直播例子
    //首先,来两个抽象接口,因为JDK要求被代理对象必须有接口
    //抽象的跳舞接口
    public  interface DanceTikTok {
       void dance();
    }
    //抽像的卖货接口
    public interface SellTikTok {
        void sell();
    }
    //实现这两个接口
    public class MyTikTok implements DanceTikTok,SellTikTok {
        @Override
        public void dance() {
            System.out.println("跳个舞......");
        }
        @Override
        public void sell() {
            System.out.println("3,2,1 上车......");
        }
    }
    //动态代理核心类,万能代理类
    public class JdkTiktokProxy<T> implements InvocationHandler {
    
        private T target;
        //接受被代理对象
        JdkTiktokProxy(T target){
            this.target = target;
        }
    
        //获取被代理对象的  代理对象
        public static<T> T getProxy(T t) {
            /**
             * ClassLoader loader, 当前被代理对象的类加载器
             * Class[] interfaces, 当前被代理对象所实现的所有接口
             * InvocationHandler h,
             *  当前被代理对象执行目标方法的时候我们使用h可以定义拦截增强方法
             */
            Object o = Proxy.newProxyInstance(
                    t.getClass().getClassLoader(),
                    t.getClass().getInterfaces(), //必须接口
                    new JdkTiktokProxy(t));
            return (T)o;
        }
        
        //定义目标方法的拦截逻辑;每个方法都会进来的
        @Override
        public Object invoke(Object proxy,
                             Method method,
                             Object[] args) throws Throwable {
            //反射执行
            System.out.println("真正执行被代理对象的方法");
            Object invoke = method.invoke(target, args);
            System.out.println("执行完返回......");
            return invoke;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • cglib动态代理(使用cglib时记得加依赖)

    • cglib在底层通过直接操作字节码的方式,使用继承的方式得到目标对象方法。

    • 代码演示:

    //要被代理增强的类
    public class MyTikTok {
        public void tiktok() {
            System.out.println("开始直播....");
        }
    }
    //cglib核心代码
    public class CglibProxy {
    
        //为任意对象创建代理
        public static<T> T createProxy(T t){
            //1、创建一个增强器
            Enhancer enhancer = new Enhancer();
    
            //2、设置要增强哪个个类的功能。增强器为这个类动态创建一个子类
            enhancer.setSuperclass(t.getClass());
    
            //3、设置回调
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj,
                                        Method method,
                                        Object[] args,
                                        MethodProxy proxy) throws Throwable {
                    //编写拦截的逻辑
                    System.out.println("cglib开场秀 .......");
                    //目标方法进行执行
                    Object invoke = proxy.invokeSuper(obj,args);
                    return invoke;
                }
            });
    
            Object o = enhancer.create();
            return (T) o;
        }
    }
    //测试
    public class CglibTest {
        public static void main(String[] args) {
            MyTikTok tikTok = new MyTikTok();
            MyTikTok proxy = CglibProxy.createProxy(tikTok);
            proxy.tiktok();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    5、组合模式

    • 定义:把一组相似的对象当作一个单一的对象。将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
      在这里插入图片描述
    //部分代码展示
    public class Menu {
    
        private Integer id;
        private String name;
        public Menu(Integer id,String name){
            this.id = id;
            this.name = name;
        }
    
        //组合模式关注点
        private List<Menu> childs = new ArrayList<>();
    
        //提供添加层级的方法
        void addChildMenu(Menu menu){
            childs.add(menu);
        }
    
        //层级遍历方法
        void printMenu(){
            System.out.println(name);
            if(childs.size() > 0){
                for (Menu child : childs) {
                    child.printMenu();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 组合模式的优缺点:
      • 优点:1、高层模块调用简单。 2、节点自由增加。
      • 缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

    6、外观模式

    • 定义:通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问
    • 举例:比如你现在想在一个城市定居落户,你需要去公安局办理身份证,监狱局认证学籍,社保局交社保,来回跑比较麻烦,这时政府推出了一个微信小程序,在小程序上就可以办理完所有的事。
      在这里插入图片描述
    //办一个新身份证
    public class Police {
        public void resgister(String name){
            System.out.println(name + "办一个新身份证");
        }
    }
    //存学籍档案
    public class Edu {
        public void school(String name){
            System.out.println(name+"存学籍档案");
        }
    }
    //开通当地社保
    public class Social {
        public void handleSocial(String name){
            System.out.println(name+"开通当地社保");
        }
    }
    //落户小程序
    public class WeiXinFacade {
    
        Police police = new Police();
        Edu edu = new Edu();
        Social social = new Social();
    
        public void handleXxx(String name){
            police.resgister(name);
            edu.school(name);
            social.handleSocial(name);
        }
    }
    //Test......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    7、享元模式

    • 定义:现在有很多对象需要大量重复的使用,考虑到使用池化技术,享元模式就是来告诉我们使用池化技术应该怎么考虑的。
    • 举例:比如一个店里有很多的服务员,如果某个服务员处于空闲状态,那么我们每个人都可以叫这些服务员为我们服务。
      在这里插入图片描述
    /**
     * 可共享和不可共享状态
     */
    public abstract class AbstractWaitressFlyweight {
    
        boolean canService = true;//能否服务
    
        //正在服务。   享元的不可共享属性留给外部进行改变的接口
        abstract void service();
        //服务完成。   享元的不可共享属性留给外部进行改变的接口
        abstract void end();
    
        public boolean isCanService() {
            return canService;
        }
    }
    /**
     * 具体享元类
     */
    @AllArgsConstructor
    public class BeautifulWaitress extends AbstractWaitressFlyweight{
        String id;//工号
        String name;//名字
        int age;//年龄
        //以上是不变的
    
        @Override
        void service() {
            System.out.println("工号:"+id+";"+name+" "+age+" 正在为您服务...");
            //改变外部状态
            this.canService = false;
        }
    
        @Override
        void end() {
            System.out.println("工号:"+id+";"+name+" "+age+" 服务结束...请给五星好评");
            this.canService = true;
        }
    }
    //享元工厂
    public class Shop {
        private static Map<String,AbstractWaitressFlyweight> pool = new HashMap<>();
        //享元,池子中有对象
        static {
            BeautifulWaitress waitress =
                    new BeautifulWaitress("1111","张三",18);
    
            BeautifulWaitress waitress2 =
                    new BeautifulWaitress("9527","李四",20);
            
            pool.put(waitress.id,waitress);
            pool.put(waitress2.id,waitress2);
        }
    
        public static AbstractWaitressFlyweight getWaitress(String name){
            AbstractWaitressFlyweight flyweight = pool.get(name);
            if(flyweight == null){
                for (AbstractWaitressFlyweight value : pool.values()) {
                    //当前共享对象能否服务
                    if(value.isCanService()){
                        return value;
                    }
                };
                return null;
            }
            return flyweight;
        }
    }
    //Test......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 享元模式优缺点
      • 优点:大大减少对象的创建,降低系统的内存,使效率提高。
      • 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

    结构型模型到这里就学习完了,下一篇:行为型模型…

  • 相关阅读:
    基于享元模式实现连接池
    Java核心知识体系8:Java如何保证线程安全性
    【毕业设计】单片机与NBIOT通信模块 - 单片机 物联网 stm32
    【数据结构基础_字符串】Leetcode 409.最长回文串
    【智能家居入门2】(MQTT协议、微信小程序、STM32、ONENET云平台)
    数据库管理-第四十四期 数据库的物理隔离(20221118)
    使用PyQt5界面设计
    这是一个很废的人的比赛经历
    Flask框架中Jinja2模板中测试器(判断值类型)
    6.qml中js的object,array数据更新不通知页面刷新问题解析
  • 原文地址:https://blog.csdn.net/NICK_53/article/details/127606964