• 21.建造者模式


    1.什么是建造者模式?

    建造者模式是一种创建型设计模式,它能让我们将创建一个复杂对象的步骤分开,一步一步的建造好我们要创建的对象架子,在最后使用时生成实例对象。 该模式允许我们使用相同的创建代码生成不同类型的对象。

    1.1 建造者模式能解决的问题

    假设有一个复杂的类,在对其进行构造时需要对很多成员变量和嵌套对象进行繁琐的初始化工作。 这些初始化代码通常深藏于一个包含众多参数且很难让人看懂的构造函数中,甚至更糟糕的时,这些初始化代码散落在系统的各个角落。
    在这种情况下,这样的类会让我们的系统的灵活性变得很差,很不利于系统的更新迭代。
    然而,建造者模式就能解决这个问题,建造者模式设计能让我们实现对复杂对象创建的可控化,让系统更加的灵活。

    1.2 具体解决方案

    建造者模式建议将对象构造代码从产品类中抽取出来,并聚合在一个名为Builder的独立对象中。

    1.2.1 详细解释

    我们如何去实施上面的解决方案呢? 下面我们以一个例子来进行讲解

    1.2.1.1 设计一个House类

    我们来思考如何设计一个House类。 建造一个简单的House, 首先我们需要建造四面墙和地板,安装房门和一套窗户,然后再建造一个屋顶。 我们知道House类中必须的属性: 墙,地板,房门,窗户,屋顶。
    UML设计如下
    在这里插入图片描述

    代码如下:

    public class House{
        //地板
        private String floor;
        //墙
        private String wall;
        //门
        private String door;
        //窗户
        private String window;
        //屋顶
        private String roof;
        public House(String floor,String wall,String door,String window,String roof){
          this.floor = floor;
          this.wall = wall;
          this.door = door;
          this.window = window;
          this.roof = roof;
        }
        public String toString(){
        System.out.println("恭喜您,建造了一个"+floor+wall+door+window+roof+" 的房子");
        return "恭喜您,建造了一个"+floor+wall+door+window+roof+" 的房子";
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    好了,我们现在可以调用House构造函数来建造一个House对象了。

    public class Demo {
        public static void main(String[] args) {
            House house = new House("木质地板","白色墙面","钢门","木窗","平顶");
            house.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    到目前为止,我们的House类的设计感觉还可以蛤,但是这样设计能应对软件开发的宿敌( change) 吗?

    1.2.1.2 扩展House类的功能

    但是如果我们想要一栋更宽敞更明亮的房屋,还要有院子和其他设施,比如暖气,排水和供电设备等,这时又该怎么办呢?

    • 最简单的一种方式是扩展House基类,然后创建一系列涵盖所有参数组合的子类。 但是这样做的后果就是我们必须面对相当数量的子类(类爆炸),并且新增任何参数都会让我们的修改变得麻烦(我们也许需要修改很多类)。

    • 另一种方式就是无需再写子类,直接修改House类,再此类中创建一个包含所有可能参数的超级构造函数,并用它来控制House对象,这种方式的确可以减少子类,但是它会造成另外一个问题: 它让House类变得复杂,用起来很麻烦。 因为通常情况下,大多数参数是没有用的,这样让我们对构造函数的调用变得十分不简洁。

    public House(String floor,String wall,String door,String window,String roof){
        this.floor = floor;
        this.wall = wall;
        this.door = door;
        this.window = window;
        this.roof = roof;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    目前的构造函数的参数已经够多了。。。

    • 为了解决这个问题,我们可以用建造者模式:
      建造者模式建议将对象构造代码从产品类中抽取出来,并聚合在一个名为Builder的独立对象中。
      就是我们新建一个HouseBuilder类,这个类用来编写生成House对象的步骤方法,我们可以灵活的用HouseBuilder类来创建我们想要的House对象。

    该模式会将对象的构造过程划分为一组步骤,每次创建对象时,我们都需要通过生成器对象执行一系列步骤,并且一些步骤的关联性不是很强,我们可以选择我们需要的步骤,最后调用getResult方法进行目标对象创建。

    package BuilderPattern.first;
    
    public class HouseBuilder {
        House house;
        //地板
        private String floor;
        //墙
        private String wall;
        //门
        private String door;
        //窗户
        private String window;
        //屋顶
        private String roof;
        
        public void buildWalls(String wall){
            this.wall = wall;
        }
        
        public void buildDoors(String door){
            this.door = door;
        }
        
        public void buildWindows(String window){
            this.window = window;
        }
        
        public void buildRoof(String roof){
            this.roof = roof;
        }
        
        public void buildFloor(String floor){
            this.floor = floor;
        }
        
        public House getResult(){
            return new House(floor,wall,door,window,roof);
        }
        
    }
    
    • 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

    下面我们的创建House就可以这样创建了:

    public class Demo {
        public static void main(String[] args) {
            System.out.println("建造器进行构造: ");
            HouseBuilder houseBuilder = new HouseBuilder();
            houseBuilder.buildDoors("木制的门");
            houseBuilder.buildRoof("金色的屋顶");
            House house1 = houseBuilder.getResult();
            house1.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    从上面两种对比我们可以总结: 建造者实际上就是将复杂的对象构造流程用固定的代码写死,然后提供了简洁的建造接口供客户端使用。

    项目地址(first提供本博客的代码,second提供一个大神写的应用实例):
    设计模式/src/main/java/BuilderPattern · 严家豆/Head first 设计模式学习 - 码云 - 开源中国 (gitee.com)

    2.建造者模式的应用

    2.1 java.lang.StringBuilder 的设计

    Java中,相信大家都认识StringBuilder, 很明显,这是一个字符串建造者。
    我们来看下它的用法吧:

    public static void main(String[] args) throws CloneNotSupportedException {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("你好\n");
        stringBuilder.append("我不好");
        System.out.println(stringBuilder.toString());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中append就是提供的一个建造方法,toString就是对应与上面的getResult。
    除此之外,StringBuilder还提供了流式建造,什么意思呢?就是下面这种用法:

    public static void main(String[] args) throws CloneNotSupportedException {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("hello").append("\n").append("haha");
        System.out.println(stringBuilder.toString());
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    那么我们怎么做到这样呢? 其实,我们只需要将每个建造方法的返回改为自类型就可以了。具体代码如下:

    package BuilderPattern.first;
    
    public class HouseBuilder {
        House house;
        //地板
        private String floor;
        //墙
        private String wall;
        //门
        private String door;
        //窗户
        private String window;
        //屋顶
        private String roof;
    
        public HouseBuilder buildWalls(String wall){
            this.wall = wall;
            return this;
        }
    
        public HouseBuilder buildDoors(String door){
            this.door = door;
            return this;
        }
    
        public HouseBuilder buildWindows(String window){
            this.window = window;
            return this;
        }
    
        public HouseBuilder buildRoof(String roof){
            this.roof = roof;
            return this;
        }
    
        public HouseBuilder buildFloor(String floor){
            this.floor = floor;
            return this;
        }
    
        public House getResult(){
            return new House(floor,wall,door,window,roof);
        }
    }
    
    • 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

    使用方式如下:

    public static void main(String[] args) {
        System.out.println("流式建造器进行构造: ");
        HouseBuilder builder = new HouseBuilder();
        builder.buildRoof("黄色屋顶").buildFloor("黑色地板");
        builder.getResult().toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    还有一些用到建造者模式的知名框架,例如okhttp3.Request.Builder, java.nio.ByteBuffer 等等。

    最后本文的内容主要吸收总结与一个很好的设计模式学习网站:
    Java 生成器模式讲解和代码示例 (refactoringguru.cn) 完全免费,推荐大家去访问学习

  • 相关阅读:
    Scala函数式编程
    L2-021 点赞狂魔
    Python判断对象是否包含对应的属性hasattr()
    咨询第三方软件测试机构报价时,软件企业应该准备什么?
    IntelliJ IDEA中Terminal终端下无法使用的问题
    post发送请求
    前端开发中的二分查找算法
    技术学习方法分享
    求一份网页设计结课大作业,要求用到html,css,javascript,的知识
    ScheduledExecutorService详解
  • 原文地址:https://blog.csdn.net/c1776167012/article/details/126277944