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


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

    首先,在创建型模式中,包括了以下几种设计模式:

    • 单例模式
    • 原型模式
    • 工厂方法模式
    • 抽象工厂模式
    • 建造者模式

    为什么会有创建型模式,它有什么用呢?

    • 创建型模式主要关注怎样创建出对象将对象的创建和使用分离
    • 它能降低系统的耦合度使用者无需关注对象的创建细节

    1、单例模式

    • 定义:一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
    • 特点 —— 做法:
      • 这个类只能有一个实例 —— 构造器私有
      • 必须自行创建这个实例 —— 自己编写实例化逻辑
      • 想整个系统提供这个实例 —— 对外提供实例方法

    关于单例模式的实现,本专栏专门有一篇文章讲解单例模式常见的七种写法,这里就不再附代码了,点击查看

    • 使用场景
      • 1、多线程中的线程池、数据库连接池等池化操作
      • 2、系统环境信息,如Java中的System类获取当前系统属性、环境变量等

    2、原型模式

    • 定义:用于创建重复的对象,同时又能保证性能
    • 举个栗子:比如我们现在要写一个查询数据库的框架MeBatis,数据库中的数据基本不会改变,每次查询到之后,把查到的所有数据封装成一个对象返回。试想一下,如果现在有10000个线程在查询相同的一个记录,那么,就会创建出10000个这样的对象,造成极大的浪费。
    • 解决例子的问题:对于上边的问题,我们可以想到,将第一次查询到的数据缓存到一个Map中,以后每次查询先去Map中看有没有,有的话直接返回,没有再查询数据库。
    • 存在问题:这样的方案仔细想想还是有问题的,如果其中的一个线程获取对象之后,将这个对象修改了,那么,后边所有的线程都将拿到一个错误的数据,造成脏缓存问题。
    • 怎么办呢:这时候就可以考虑原型模式,当线程拿到数据对象之后,我们不直接返回,而是返回一个克隆对象,那么无论这个线程如何修改,都不会影响缓存数据,后边的线程就会获取到正确的数据
    • 代码演示:10000个线程查询数据库中一个User数据(模拟,部分主要代码)
    //User:实现Cloneable接口,重写clone()方法
    public class User implements Cloneable {
    
        private String username;
        private Integer age;
    
        /**
         * 再创建一个人,赋予我的所有属性
         * @return
         * @throws CloneNotSupportedException
         */
        @Override
        protected Object clone() throws CloneNotSupportedException {
            User user = new User();
            user.setUsername(username);
            user.setAge(age);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    public class Mebatis {
        //缓存user
        private Map<String,User> userCache = new HashMap<>();
    
        //模拟从数据库查数据
        public User getUser(String username) throws Exception {
            User user = null;
            //缓存中没有
            if(!userCache.containsKey(username)){
                //查询数据库
                user = getUserFromDb(username);
            }else {
                //从缓存中直接拿,脏缓存问题
                //原型已经拿到,但是不能直接给。
                user = userCache.get(username);
                System.out.println("从缓存中拿到的是:"+user);
                //从这个对象快速得到一个克隆体(克隆人)==原型模式
                user = (User) user.clone();
            }
            return user;
        }
    
        private User getUserFromDb(String username) throws Exception{
            System.out.println("从数据库查到:"+username);
            User user = new User();
            user.setUsername(username);
            user.setAge(18);
            //给缓存中放一个clone
            userCache.put(username, (User) user.clone());
            return user;
        }
    }
    
    • 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
    • 总结:本体给外部提供一个克隆体使用

    3、工厂模式

    • 定义:提供一种创建对象的最佳方式,使我们不必关系创建细节,只需根据不同情况获得不同产品
    1.简单工厂
    • 在只有几个产品的情况下,适合使用简单工厂模式,比如现在有一个汽车工厂,只生产两种车
      在这里插入图片描述
    • 代码示例:
    /**
     * 简单工厂
     * 1、产品数量极少
     */
    public class WuLinSimpleFactory {
        
        public AbstractCar newCar(String type){
    
            //核心方法:一切从简
            if("van".equals(type)){
                // 钣金、喷漆、放发动机、申请环保
                return new VanCar();
            }else if("mini".equals(type)){
                return new MiniCar();
            }
            return null;//没有产品
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    /**
     * 抽象的产品
     */
    public abstract class AbstractCar {
    
        String engine;
        public abstract void run();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //具体产品
    public class MiniCar extends AbstractCar{
    
        public MiniCar(){
            this.engine = "单引擎";
        }
        @Override
        public void run() {
            System.out.println(engine+"--> 发车...");
        }
    }
    
    /**
     * 具体产品
     */
    public class VanCar extends AbstractCar{
        public VanCar(){
            this.engine = "双引擎";
        }
        @Override
        public void run() {
            System.out.println(engine+"-->发车");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 缺点:扩产性差,违反开闭原则
    2.工厂方法
    • 改进:将工厂提升一个等级,创建一个抽象的工厂,然后利用多个具体的工厂,每个工厂生产一种商品,想要扩展产品时,只需要添加具体的工厂,提高程序的可扩展性。
      在这里插入图片描述
    • 主要代码:
    /**
     * 抽象工厂
     */
    public abstract class AbstractCarFactory {
    
        public abstract AbstractCar newCar();
        //我能造什么.....
    }
    
    /**
     * minicar分厂,具体的产品工厂,可以扩展添加
     */
    public class WulinMinCarFactory extends AbstractCarFactory{
        @Override
        public AbstractCar newCar() {
            return new MiniCar();
        }
    }
    //具体的产品,有某一具体的工厂生产
    public class MiniCar extends AbstractCar {
        
        public MiniCar(){
            this.engine = "迷你发动机";
        }
        @Override
        public void run() {
            System.out.println(engine+"--> 发车...");
        }
    }
    
    • 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
    • 缺点:品类单一,现在只能造车,我要是想造口罩怎么办
    3.抽象工厂
    • 改进:为了满足多个产品,继续向上抽象
      在这里插入图片描述
    • 主要代码
    /**
     * 总集团规范,最上层抽象工厂,定义可以造啥
     */
    public abstract class WulinFactory {
    
        abstract AbstractCar newCar();
        abstract AbstractMask newMask();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    /**
     * wulin 汽车子集团,只负责造汽车,口罩直接返回null
     */
    public  abstract  class WulinCarFactory extends WulinFactory{
        @Override
        abstract  AbstractCar newCar();
    
        @Override
        AbstractMask newMask() {
            return null;
        }
    }
    
    /**
     * wulin口罩子集团,只负责造口罩,汽车直接返回null
     */
    public abstract class WulinMaskFactory extends WulinFactory{
        @Override
        AbstractCar newCar() {
            return null;
        }
    
        abstract AbstractMask newMask();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    /**
     * 抽象产品
     */
    public abstract class AbstractCar {
    
        String engine;
        public abstract void run();
    }
    
    /**
     * 抽象产品
     */
    public abstract class AbstractMask {
    
        Integer price;
        public abstract void protectedMe();
    }
    //具体产品省略,代码太多了......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    /**
     * 口罩子集团建分厂:只造口罩,且武汉的厂只造N95口罩
     */
    public class WulinWuHanMaskFactory  extends WulinMaskFactory{
    
        @Override
        AbstractMask newMask() {
            return new N95Mask();
        }
    }
    
    /**
     * 口罩子集团建分厂:只造口罩,且杭州的厂造普通外科口罩
     */
    public class WulinHangZhouMaskFactory extends WulinMaskFactory {
    
        @Override
        AbstractMask newMask() {
            return new CommonMask();
        }
    }
    //汽车子集团建汽车各地分厂类似......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4、建造者模式

    • 定义:创建的东西细节复杂,还必须暴露给使用者。屏蔽过程而不屏蔽细节
    • 举例(比如我们现在要定制一台手机)
    //我们想要定制的手机
    @Getter
    @ToString
    public class Phone {
    
        protected String cpu;
        protected String mem;
        protected String disk;
        protected String cam;
    }
    
    /**
     * 抽象建造者,定义我们定制化的参数,返回最终的手机
     */
    public abstract class AbstractBuilder  {
    
        Phone phone;
        abstract AbstractBuilder customCpu(String cpu);
        abstract AbstractBuilder customMem(String mem);
        abstract AbstractBuilder customDisk(String disk);
        abstract AbstractBuilder customCam(String cam);
    
        Phone getProduct(){
            return phone;
        }
    }
    
    //赋值后返回自己,方便链式调用
    public class XiaomiBuilder  extends AbstractBuilder{
        public XiaomiBuilder(){
            phone = new Phone();
        }
        
        @Override
        AbstractBuilder customCpu(String cpu) {
            phone.cpu = cpu;
            return this;
        }
    
        @Override
        AbstractBuilder customMem(String mem) {
            phone.mem = mem;
            return this;
        }
    
        @Override
        AbstractBuilder customDisk(String disk) {
            phone.disk = disk;
            return this;
        }
    
        @Override
        AbstractBuilder customCam(String cam) {
            phone.cam = cam;
            return this;
        }
    }
    
    • 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

    到这里,创建型的几种设计模式就学习完了,下一篇结构型设计模式…

  • 相关阅读:
    Linux系统中sh脚本编写
    JMM内存模型 / JVM 内存区域
    【JavaWeb - 网页编程】一 HTML
    Taro 小程序开启wxml代码压缩
    Java如何读取矢量图并显示在GUI上面?
    易基因|文献科普:MeRIP-seq揭示m6A RNA甲基化改变导致亨廷顿病(HD)小鼠海马记忆障碍
    Docker使用及本地Yolov5打包教程
    深度学习(part3)--深度学习框架tensorflow
    【数据结构】树与二叉树(二):树的表示C语言:树形表示法、嵌套集合表示法、嵌套括号表示法 、凹入表示法
    前端RSA加解密(支持超长分段)
  • 原文地址:https://blog.csdn.net/NICK_53/article/details/127483419