为了账号安全,请及时绑定邮箱和手机立即绑定

建造者模式

建造者模式是一种创建型设计模式,它提供了一种逐步构建复杂对象的方式。它将对象的构建过程与其表示形式分离,使得相同的构建过程可以创建不同类型和表示的复杂对象。

现在,问题就来了,为什么我们需要使用建造者模式,我们遇到了什么问题?

在创建包含许多属性的对象时,可能会遇到许多问题。

  1. 我们需要传递很多参数来创建对象。
  2. 有些参数可以不填。
  3. 工厂类负责创建对象。如果对象创建起来比较复杂,那么这些复杂性都将由工厂类承担。

所以在建造者模式中提供了几个优点,特别是在构建复杂对象时,并且可以逐步创建对象,最终返回具有所需属性值的完整对象。

建造者模式可以用订一个定制比萨来类比,比如订一份定制比萨。

没有使用建造者模式的场景:

想象你去了一家披萨店 🍕,只能选择一种方式点披萨:你需要一次性指定所有内容——包括面团、尺寸、酱料、配料和额外添加的。如果你漏掉了任何细节或错误地选择了某些选项,披萨 🍕 可能不会如你所愿,你可能需要从头再来一遍。这就像用一个有许多参数的构造函数——容易出错且很难管理,尤其是在有很多选择项或可选项时。

没有Builder的示例

"我要一份披萨 🍕,薄饼底,中等大小,番茄沙司,蘑菇,奶酪 🧀,不要放橄榄 🍅,并多加一些罗勒 🍀。"
使用建造者模式的情境:
现在,想象一个更灵活的方式,披萨师傅会一步步地询问你的喜好:

首先,他们会问你想要什么尺寸的披萨。
接着,他们会问你想要什么样的饼底。
然后,他们会问你想要什么配料。
最后,他们会问你是否想要一些额外的东西,比如额外的奶酪或特定的酱料。
在每一步中,你都可以根据自己的喜好做出选择。
最后,你会得到一个完全按照你的口味定制的披萨。
你不必担心一下子记住所有的细节。
每个选择都建立在前一个选择的基础上。

比如说,Builder

"我要一个中等大小的比萨。"
"我要薄饼底的。"
"加点蘑菇和奶酪。"
"不要放橄榄,好吗?"
"再加点罗勒叶。"
建造者模式这种设计模式,让你可以一步一步地构建对象(或者比萨!),这样更灵活,也更不容易出错。

以下是我观察到建造者模式如何优于其他方法(如 telescoping 构造器或 setter)的几个优点:

  • 建造者模式可以避免参数过多的问题。
  • 通过封装构建过程,建造者模式使得代码更加清晰和易于维护。
  • 建造者模式允许在不一次性提供所有必要信息的情况下逐步构建对象。

让具有多个参数的对象创建变得更简单

  • 当一个对象有许多可选或必需的参数时,使用标准构造函数可能会变得混乱且难以管理,容易出错。
  • 建造者模式提供了一种清晰且逐步的方法来创建对象,使得只需设置必要的属性,而无需担心参数顺序,变得更加简单。

例如:而不是这个令人困惑的构造器。

    以下代码创建了一个新的Car对象,型号为特斯拉Model S,生产年份为2024年,颜色为红色,且具有自动驾驶功能。
    const car = new Car('Tesla', 'Model S', 2024, 'Red', true, false);

进入全屏模式 退出全屏模式

你得到了这个可读的代码:

const car = new CarBuilder('Tesla', 'Model S')
              .setYear(2024)
              .setColor('红色')
              .addGPS()
              .build();

点击全屏,退出全屏

2). 支持链式调用(流畅接口)

  • 流畅接口允许方法返回构建器对象本身,从而支持链式调用。这使代码更加直观易读,因为它提供了一个逻辑清晰的逐步过程来设置属性。

好处:每次方法调用都像一组构建对象的指令,让代码更易读。

3). 妥善处理可选参数

  • 在使用构造函数时,很难处理可选参数,通常需要传递 null 值或创建多个重载的构造函数。
  • 建造者模式允许你只包含你需要的参数,而无需担心为每个参数提供默认值或占位。

(注:原文中的第二条建议中的“而无需担心”在修改后的句子中只保留了一次以避免重复,因此在输出中只保留了一次。)

不使用构建器(不通过构造函数):

const car = new Car('特斯拉', 'Model S', 2024, null, true, false); // 有点奇怪

全屏模式 退出全屏

使用Builder:

    const car = new CarBuilder('特斯拉', 'Model S')
                  .setYear(2024)
                  .addGPS()
                  .build();  // 易于理解

进入全屏模式,退出全屏模式

4). 促进不变性特点

  • 一旦对象被创建,它通常是不可变的——创建后无法更改其状态。这确保构建的对象处于有效和稳定的状态,使你的代码更安全,更不易出错。

优势:在调用 build() 方法之后,对象会完全构建,减少了因对象未完全构建而引发错误的可能性。

5). 更容易扩展和修改

  • 建造者模式很容易扩展。当需要添加新的属性或功能时,建造者可以添加新功能而不影响现有代码。不用建造者:如果你给构造函数添加了一个新参数,你就得修改所有相关的对象实例化代码。

使用构建器:只需在构建器中新增一个属性设置,这样不会影响对象的构建过程。

例如:添加一种发动机类型:

const car = new CarBuilder('特斯拉', 'Model S')  // 特斯拉 (Tēsīlā)
                  .setYear(2024)
                  .setColor('Red')
                  .setEngineType('电动')  // 电动 (Diàn dòng)
                  .build();

进入全屏,退出全屏

  1. 提高可读性和易维护性
  • 建造者模式通过将构建过程分解为小且易懂的步骤来提高代码的可读性。
  • 这使得代码更具有自文档性——你可以清楚地看到一个对象是如何被构建的,而无需查阅每个构造函数参数的含义。好处是,因为每个方法都清楚地定义了它修改的对象部分,因此代码更容易维护和修改。
  1. 防止对象不一致
  • 构建过程确保在构建对象之前设置了所有必需的参数,防止构建不一致或不完整的对象。
  • 此验证过程在构建时发生,确保对象在创建时处于有效状态或正确状态。

不使用Builder:您可能会创建一些不完整或未正确初始化的对象。

使用建造者模式可以确保对象的一致性和完整性。

8). 减少了构造函数的重载需求

  • 没有建造者模式的情况下,你可能会遇到多个构造函数的重载版本来适应不同参数组合。好处:使用建造者模式,你只需要一个构造函数,有一个清晰灵活的构建过程。

我们通过一个现实生活中的例子来对比有无构建者模式创建汽车对象的过程。在这个例子中,汽车对象包含多个属性,其中一些属性是可选的,有些属性可以不用指定。

没有使用建造者模式

1). 使用具有多个参数的构造函数

    class Car {
      constructor(make, model, year, color, hasGPS, hasSunroof) {
        this.make = make;
        this.model = model;
        this.year = year;
        this.color = color;
        this.hasGPS = hasGPS || false;     // 可选参数
        this.hasSunroof = hasSunroof || false; // 可选参数
      }

      describe() {
        return `${this.year} ${this.make} ${this.model},颜色为 ${this.color},是否配备 GPS: ${this.hasGPS},天窗功能: ${this.hasSunroof}`;
      }
    }

    // 创建一个对象
    const car = new Car('Tesla', 'Model S', 2024, 'Red', true, false);
    console.log(car.describe());

全屏模式 退出全屏

建造模式

2). 使用建造者模式,这种方法

    // Car 类保持简单,没有复杂的构造
    class Car {
      constructor(builder) {
        this.make = builder.make;
        this.model = builder.model;
        this.year = builder.year;
        this.color = builder.color;
        this.hasGPS = builder.hasGPS;
        this.hasSunroof = builder.hasSunroof;
      }

      describe() {
        return `${this.year} ${this.make} ${this.model} ${this.color}颜色的,带有GPS: ${this.hasGPS} 和 天窗功能: ${this.hasSunroof}`;
      }
    }

    // CarBuilder 类用于逐步构建对象

    class CarBuilder {
      constructor(make, model) {
        this.make = make;
        this.model = model;
      }

      setYear(year) {
        this.year = year;
        return this;
      }

      setColor(color) {
        this.color = color;
        return this;
      }

      addGPS() {
        this.hasGPS = true;
        return this;
      }

      addSunroof() {
        this.hasSunroof = true;
        return this;
      }

      build() {
        return new Car(this); // 构建 Car 对象
      }
    }

    // 使用构建器创建对象

    const car = new CarBuilder('Tesla', 'Model S')
      .setYear(2024)
      .setColor('Red')
      .addGPS()
      .build();

    console.log(car.describe());

全屏,退出全屏

我很想知道你是如何将这些想法应用到工作中的,在下面的评论中分享你的想法或疑问——我很想听听你的分享。

感谢你和我一起踏上这次学习之旅!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消