• 「设计模式」建造者模式


    「设计模式」建造者模式

    一、概念

    建造者模式(英:Builder Pattern)是一种创建型设计模式,又名:生成器模式。GOF 给建造者模式的定义为:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这句话说的比较抽象,其实解释一下就是:将建造复杂对象的过程和组成对象的部件解耦。

    建造者模式也属于创建者模式,它提供了一种创建对象的最佳方式
    定义讲一个复杂对象的构建表示分离,使得同样的构建过程,可以创建不同的表示
    主要作用用户不需要知道,对象的的建造过程和细节,就能创建复杂的对象
    例子1.工厂(建造者模式):负责造汽车(组装过程和细节在工厂内) 2.购买者(用户):只需要说出型号(对象的类型和内容),就能直接购买(不用知道汽车怎么组装的)

    用户只需要给出复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(隐藏内部的过程和细节)

    image-20221101224147347

    二、实现

    //1.-----------抽象的建造者:定义产品的建造方----------法
    public abstract class Builder {
        abstract void buildA(); //步骤一
        abstract void buildB(); //步骤二
        //建造完成,得到产品:
        abstract Product getProduct();
    }
    
    //2.------- 定义产品,产品与建造者无必然联系 ---------
    public class Product {
        private String buildA;  //步骤一搞定
        private String buildB;  //步骤二搞定
    
        //get set 方法
        public String getBuildA() { return buildA; }
        public void setBuildA(String buildA) { this.buildA = buildA; }
        public String getBuildB() { return buildB; }
        public void setBuildB(String buildB) { this.buildB = buildB; }
        //toString方法
        @Override
        public String toString() {
            return "Product{" + "buildA='" + buildA + '\'' + ", buildB='" + buildB + '\'' + '}';
        }
    }
    
    /*
    3.----------工人继承Builder类,实现方法-------
    	不同的工人,可以按照构建者模式,生产不同规格的产品
    */
    public class Worker extends Builder {
        private Product product;    //产品
    
        /*--- 这一步很重要,工人负责创建产品,而不是外部传进来 --*/
        public Worker() {
            this.product = new Product();
        }
        //执行第一步
        @Override
        void buildA() {
            product.setBuildA("步骤A");
            System.out.println("步骤A");
        }
        //执行第二步
        @Override
        void buildB() {
            product.setBuildB("步骤B");
            System.out.println("步骤B");
        }
        //返回完整产品
        @Override
        Product getProduct() {
            return product;
        }
    }
    //4.----------指挥者,指挥工人生产产品
    public class Director {
        //****指挥工人,按照顺序构建产品****
        public Product build(Builder builder){
        	//*****可以控制顺序*******
            builder.buildB();
            builder.buildA();
            return builder.getProduct();
        }
    }
    
    //5.--------测试:按照指挥者指定的顺序,得到产品--------
    public static void main(String[] args) {
        Director director = new Director();
        Product product = director.build(new Worker());
        System.out.println(product);
    }
    
    • 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
    • 70
    • 71

    • 上面是Builder模式的常规用法,指挥类Director在构造者模式中作用很大:指挥具体构建者(Worker)如何构建产品,控制先后次序,并返回完整的产品类(Product)

    • 通过静态内部类方式,实现零件无序装配构造,这种方式更灵活、更符合定义。内部有复杂对象的默认实现,使用时也可自定义更改内容,并且无需改变构造方式。
    • 比如:饭店的套餐,服务员(具体构造者),可以默认几种菜品(零件)组成套餐(产品),客户(使用者)也可要求更换其中的菜品(零件)
    • 比第一种少了指挥者,客户就是指挥者

    //1.------------ 产品,属性有默认值 ------------
    public class Product {
        private String foodA = "小龙虾";
        private String foodB = "大白菜";
        //get set 方法
        public String getFoodA() { return foodA; }
        public void setFoodA(String foodA) { this.foodA = foodA; }
        public String getFoodB() { return foodB; }
        public void setFoodB(String foodB) { this.foodB = foodB; }
        @Override
        public String toString() {
            return "Product{" + "foodA='" + foodA + '\'' + ", foodB='" + foodB + '\'' + '}';
        }
    }
    
    //2.---------- 抽象构造者,定义生产产品有哪些步骤----------
    public abstract class Builder {
        //构造方法
        abstract Builder buildA(String msg);
        abstract Builder buildB(String msg);
        //返回产品
        abstract Product getProduct();
    }
    
    //3.---------- 具体构造者(服务员)----------
    public class Worker extends Builder {
        private Product product;
        //产品在构造器中创建,而不是外部传进来
        public Worker() {
            product = new Product();
        }
        @Override
        Builder buildA(String msg) {
            product.setFoodA(msg);
            return this;    //返回当前对象
        }
        @Override
        Builder buildB(String msg) {
            product.setFoodA(msg);
            return this;    //返回当前对象
        }
        @Override
        Product getProduct() {
            return product;
        }
    }
    //4.---------- 测试:----------
    public static void main(String[] args) {
        //服务员
        Builder worker = new Worker();
        //输出默认产品
        System.out.println(worker.getProduct());
        //链式编程(执行方法之后,依然返回当前对象:worker)
        Product product = worker.buildA("大豆角").buildB("冰淇淋").getProduct();
        System.out.println(product);
    }
    输出结果:
    Product{foodA='小龙虾', foodB='大白菜'}
    Product{foodA='大豆角', foodB='冰淇淋'}
    
    
    • 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

    到这里,一个建造者模式已经完成了,是不是很简单?


    再回到之前的需求,看看我们是否都满足?

    由于建造角色的过程比较复杂,其中还有相互依赖关系(如脸部依赖于头部),所以我们使用建造者模式将将建造复杂对象的过程和组成对象的部件解耦。这样既保证了基本属性全都一致(这里的一致指的是该包含的应该全都包含)也封装了其中的具体实现细节。

    同时,在修改某个具体角色的时候我们只需要修改对应的具体角色就可以了,不会影响到其他角色。

    如果需要新增角色,只要再增加一个具体建造者,并在该建造者中写好具体细节的建造部分代码就OK了。

    三、建造者模式的优缺点

    优点

    建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。

    在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

    可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

    其次,建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则

    缺点

    建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

    如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

    四、适用环境

    在以下情况下可以使用建造者模式:

    需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。

    需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

    对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。

    隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

    五、建造者模式与工厂模式的区别

    我们可以看到,建造者模式与工厂模式是极为相似的,总体上,建造者模式仅仅只比工厂模式多了一个”指挥者”的角色。在建造者模式的类图中,假如把这个导演类看做是最终调用的客户端,那么图中剩余的部分就可以看作是一个简单的工厂模式了。

    与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。

    也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。

    建造者模式与工厂模式类似,适用的场景也很相似。一般来说,如果产品的建造很复杂,那么请用工厂模式;如果产品的建造更复杂,那么请用建造者模式。哈哈哈。。。

    六、总结

    建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    在建造者模式的结构中引入了一个指挥者类,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。

    参考资料

    大话设计模式

    深入浅出设计模式

    建造者模式

    Hollis

    狂神说

  • 相关阅读:
    Python知识汇总
    node.js及npm的基本理解
    Flink SQL TopN语句详解
    windows上安装MongoDB 4.4.16(详细教程)
    输入一个三位数,使用递归实现,按位输出
    Git Commit Message 规范实践
    泛型进阶:通配符
    CSS总结第七天
    仅用5000行代码,在V853上AI渲染出一亿幅山水画
    RL强化学习总结(三)——Q-Learning算法
  • 原文地址:https://blog.csdn.net/u014571143/article/details/127644439