• 【设计模式】建造者模式


    创建一个对象,必定要设置该对象的属性,简单的对象还好,但一个复杂的对象具有很多复杂的属性,每次我们创建一个复杂对象后还需要设置它大量的属性,这显然浪费了时间。为了解决这个问题,我们可以考虑给该对象的属性设置一个有效的默认值,这样就有了一套默认的模板,我们创建对象时就只需要创建这个模板就行。但是,我们想要要多套默认的模板该怎么办呢?这个时候就可以通过设计模式来实现。

    介绍

    建造者模式主要解决一个复杂对象的创建工作,将其构建过程与表示过程进行分离,使其在相同的构建过程中可以创建出不同的模板。

    工厂模式与建造者的区别

    工厂模式和建造者模式都能帮助我们创建一个完整的对象,但工厂模式管理多个类,为多个类创建对象。而建造者模式只管理一个类,更重视对一个类的对象进行构建。

    建造者模式的使用

    以汉堡店的套餐为例,汉堡店中有多个套餐,每个套餐中有多个产品。现在我要创建一个套餐对象,但我不想一遍遍的设置该对象名称是什么,有哪些东西,价格是多少。于是我为套餐类添加一个建造者类来管理套餐对象。

    类图

    在这里插入图片描述

    实现方法

    第一步,创建食物类和套餐类

    食物类
    package 设计模式.建造者模式;
    
    public class 食物类 {
        private String 名称;
        private Double 价格;
    
        public void 设置名称(String 名称) {
            this.名称 = 名称;
        }
    
        public void 设置价格(Double 价格) {
            this.价格 = 价格;
        }
    
        public String 获取名称() {
            return 名称;
        }
    
        public Double 获取价格() {
            return 价格;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    套餐类
    package 设计模式.建造者模式;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class 套餐类 {
        private String 套餐名;
        private List<食物类> 食物列表 = new ArrayList<>();
        private Double 价格;
    
        public void 设置套餐名(String 套餐名){
            this.套餐名 = 套餐名;
        }
    
        public void 添加食物(食物类 食物){
            食物列表.add(食物);
        }
    
        public void 展示套餐(){
            System.out.println(套餐名 + ":");
            价格 = 0d;
            for (食物类 食物 : 食物列表) {
                System.out.println("  " + 食物.获取名称() + "\t单价:" + 食物.获取价格());
                价格 += 食物.获取价格();
            }
            System.out.println("套餐价格:" + 价格);
        }
    }
    
    • 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

    第二步,创建建造者类

    建造者类
    package 设计模式.建造者模式;
    
    public class 建造者类 {
    
        public 套餐类 构造小霸王套餐(){
            套餐类 小霸王 = new 套餐类();
            小霸王.设置套餐名("小霸王套餐");
            // 创建食物——霸王鸡腿堡
            食物类 食物 = new 食物类();
            食物.设置名称("霸王鸡腿堡");
            食物.设置价格(12d);
            // 为套餐添加食物
            小霸王.添加食物(食物);
            // 创建食物——薯条
            食物 = new 食物类();
            食物.设置名称("薯条");
            食物.设置价格(10d);
            // 为套餐添加食物
            小霸王.添加食物(食物);
            // 创建食物——可乐
            食物 = new 食物类();
            食物.设置名称("中杯可乐");
            食物.设置价格(4.5);
            // 为套餐添加食物
            小霸王.添加食物(食物);
            return 小霸王;
        }
    
        public 套餐类 构造大霸王套餐(){
            套餐类 大霸王 = new 套餐类();
            大霸王.设置套餐名("大霸王套餐");
            // 创建食物——霸王鸡腿堡
            食物类 食物 = new 食物类();
            食物.设置名称("双层霸王鸡腿堡");
            食物.设置价格(16d);
            // 为套餐添加食物
            大霸王.添加食物(食物);
            // 创建食物——薯条
            食物 = new 食物类();
            食物.设置名称("薯条");
            食物.设置价格(10d);
            // 创建食物——上校鸡块
            食物 = new 食物类();
            食物.设置名称("上校鸡块");
            食物.设置价格(10d);
            // 为套餐添加食物
            大霸王.添加食物(食物);
            // 创建食物——可乐
            食物 = new 食物类();
            食物.设置名称("大杯可乐");
            食物.设置价格(4.5);
            // 为套餐添加食物
            大霸王.添加食物(食物);
            return 大霸王;
        }
    }
    
    • 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

    第三步,创建测试类测试

    测试类
    package 设计模式.建造者模式;
    
    public class 测试类 {
        public static void main(String[] args) {
            套餐类 套餐 = new 建造者类().构造小霸王套餐();
            套餐.展示套餐();
            System.out.println();
            套餐 = new 建造者类().构造大霸王套餐();
            套餐.展示套餐();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    测试结果

    image-20221121210231066



    建造者模式的进一步优化使用

    ​ 通过上面的方法,我的确实现了构造与展示分离(所有的构造操作都在建造者类中完成,调用者无需关心构造的过程)。但此模式的结构还能优化,当前我们每添加一种套餐,就需要在建造者类中创建大量的食物对象,而这些食物对象(部分)价格和名称都是不变的,我们改变的只是套餐这个整体罢了,于是我想可以让建造者管理部分的创建,也就是添加各个食物,再创建一个指挥者类来管理整体套餐的创建,我们现在只用和指挥者沟通即可完成套餐的创建。

    类图

    image-20221121215651872

    实现方法

    第一步,创建食物类和套餐类

    ​ 方法和之前的一样,没有改动

    第二步,创建建造者类

    套餐建造者类
    package 设计模式.建造者模式.优化;
    
    import 设计模式.建造者模式.套餐类;
    import 设计模式.建造者模式.食物类;
    
    public class 套餐建造者类 {
    
        private 套餐类 套餐;
    
        private 套餐建造者类(){
            // 可以直接在此方法中新建套餐,用new方法创建建造者。但我喜欢使用静态方法来创建,实现全部链式调用
        }
    
        /**
         * 初始化操作
         * @return
         */
        public static 套餐建造者类 准备套餐(){
            套餐建造者类 建造者 = new 套餐建造者类();
            建造者.套餐 = new 套餐类();
            return 建造者;
        }
    
        public 套餐建造者类 设置套餐名(String 套餐名){
            套餐.设置套餐名(套餐名);
            return this;
        }
    
        private void 添加食物(String 食物名, Double 价格){
            食物类 食物 = new 食物类();
            食物.设置名称(食物名);
            食物.设置价格(价格);
            套餐.添加食物(食物);
        }
    
        public 套餐建造者类 添加霸王鸡腿堡(){
            添加食物("霸王鸡腿堡",12d);
            return this;
        }
    
        public 套餐建造者类 添加双层霸王鸡腿堡(){
            添加食物("双层霸王鸡腿堡",16d);
            return this;
        }
    
        public 套餐建造者类 添加薯条(){
            添加食物("薯条",10d);
            return this;
        }
    
        public 套餐建造者类 添加上校鸡块(){
            添加食物("上校鸡块",10d);
            return this;
        }
    
        public 套餐建造者类 添加可乐(String 大小){
            // 模拟更复杂的创建方法
            String 名称 = 大小 + "可乐";
            Double 价格 = 4d;
            价格 = 大小.equals("小杯") ? 2d : 价格;
            价格 = 大小.equals("大杯") ? 5d : 价格;
            添加食物(名称, 价格);
            return this;
        }
        
        public 套餐类 建造完成(){
            return 套餐;
        }
    }
    
    • 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

    第三步,创建指挥者类

    指挥者类
    package 设计模式.建造者模式.优化;
    
    import 设计模式.建造者模式.套餐类;
    
    public class 指挥者类 {
    
        public 套餐类 构造小霸王套餐(){
            return 套餐建造者类
                    .准备套餐()
                    .设置套餐名("小霸王套餐")
                    .添加霸王鸡腿堡()
                    .添加薯条()
                    .添加可乐("小杯")
                    .建造完成();
        }
    
        public 套餐类 构造大霸王套餐(){
            return 套餐建造者类.准备套餐()
                    .设置套餐名("大霸王套餐")
                    .添加双层霸王鸡腿堡()
                    .添加上校鸡块()
                    .添加可乐("中杯")
                    .建造完成();
        }
    
        public 套餐类 构造至尊大霸王套餐(){
            return 套餐建造者类.准备套餐()
                    .设置套餐名("至尊大霸王套餐")
                    .添加双层霸王鸡腿堡()
                    .添加上校鸡块()
                    .添加薯条()
                    .添加可乐("大杯")
                    .建造完成();
        }
    }
    
    • 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

    第四步,创建测试类测试

    测试类
    package 设计模式.建造者模式;
    
    import 设计模式.建造者模式.优化.指挥者类;
    
    public class 测试类 {
        public static void main(String[] args) {
            套餐类 套餐 = new 指挥者类().构造小霸王套餐();
            套餐.展示套餐();
            System.out.println();
            套餐 = new 指挥者类().构造大霸王套餐();
            套餐.展示套餐();
            System.out.println();
            套餐 = new 指挥者类().构造至尊大霸王套餐();
            套餐.展示套餐();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    测试结果

    image-20221121220128148

    建造者模式的链式调用

    ​ 使用链式调用方法可以非常方便的完成一个对象的创建。

    Computer

    package 设计模式.建造者模式.链式调用;
    
    public class Computer {
    
        private String cpu;
        private String videoCard;
        private String memory;
        private String storageSpaces;
    
        private Computer(Builder builder){
                cpu = builder.cpu;
                videoCard = builder.videoCard;
                memory = builder.memory;
                storageSpaces = builder.storageSpaces;
        }
    
        @Override
        public String toString() {
            return "Computer{" +
                    "cpu='" + cpu + '\'' +
                    ", videoCard='" + videoCard + '\'' +
                    ", memory='" + memory + '\'' +
                    ", storageSpaces='" + storageSpaces + '\'' +
                    '}';
        }
    
        public static class Builder{
            private String cpu;
            private String videoCard;
            private String memory;
            private String storageSpaces;
    
            public Builder setCpu(String cpu) {
                this.cpu = cpu;
                return this;
            }
    
            public Builder setVideoCard(String videoCard) {
                this.videoCard = videoCard;
                return this;
            }
    
            public Builder setMemory(String memory) {
                this.memory = memory;
                return this;
            }
    
            public Builder setStorageSpaces(String storageSpaces) {
                this.storageSpaces = storageSpaces;
                return this;
            }
    
            public Computer build(){
                return new Computer(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
    • 58

    Test

    package 设计模式.建造者模式.链式调用;
    
    public class Test {
        public static void main(String[] args) {
            Computer computer = new Computer.Builder().setCpu("CORE i7-9750")
                    .setMemory("16GB")
                    .setVideoCard("GTX1660 Ti 6G")
                    .setStorageSpaces("1TB SSD + 1TB HDD")
                    .build();
            System.out.println(computer);
        }
    }
           }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    链表剖析及自己手撸“单链表“实现基本操作(初始化、增、删、改等)
    决策树-入门
    009. 递增子序列
    Scala第三章节
    HIVE无法启动问题
    【计算思维】少儿编程蓝桥杯青少组计算思维题考试真题及解析B
    docker网段冲突导致主机网络异常处理
    解密Vue中key的神奇原理:优化列表渲染效率的关键策略!
    【Jmeter】基于JMeter开展性能测试(插件、监控、分布式压测)!
    唯品会:高利润,慢增长?
  • 原文地址:https://blog.csdn.net/weixin_45634261/article/details/127973961