建造者设计模式
建造者模式是一种创建型设计模式,它提供了一种逐步构建复杂对象的方法。它将对象的构造与其表示分离,允许相同的构造过程创建不同类型和表示的对象。
现在的问题是,为什么我们需要建造者设计模式,我们面临什么问题?
当对象包含许多属性时,创建对象时存在许多问题:
- 我们必须传递许多参数来创建对象
- 某些参数可能是可选的
- 工厂类负责创建对象。如果对象很重,那么所有复杂性都由工厂类承担。
因此,构建器模式提供了多种优势,特别是在处理复杂对象的构造时,用于逐步创建对象并最终返回具有所需属性值的对象。
可以用一个简单的类比来解释建造者模式:订购定制披萨。
没有使用建造者模式的场景:
想象一下,你去一家披萨店🍕,订购披萨时只有一个选项:你必须一次性指定所有内容——饼底类型、大小、酱汁、配料和附加项。如果你遗漏或错误地指定了某个选项,披萨🍕可能就做不出你想要的样子,你就得从头开始重新做一遍。这就像使用一个包含许多参数的构造函数——它很容易出错,而且难以管理,尤其是在有很多选项或可选项的情况下。
不使用 Builder 的示例:
“我想要一个薄皮披萨🍕,中等大小,番茄酱,蘑菇,奶酪🧀,不要橄榄🍅,再加点罗勒🍀。” 如果你漏掉了什么,就得重新来!
建造者模式的场景:
现在,想一个更灵活的方法,披萨师傅会一步一步地询问你的喜好:
首先,他们会询问你想要什么尺寸。
然后,他们会询问饼底类型。
接下来,他们会询问配料。
最后,他们会询问你是否需要额外添加,比如额外的奶酪或特定的酱汁。
每一步,你都可以根据自己的喜好做出选择,最终,你会得到一个完全符合你口味的披萨。你不必费心记住或一次性指定所有选项。每个选择都建立在前一个选择的基础上。
Builder 示例:
“我想要一个中号披萨。”
“我想要薄皮的。”
“加蘑菇和奶酪。”
“不要橄榄。”
“加点罗勒。”
建造者模式允许循序渐进、结构化的过程,在这个过程中你可以逐步构建你的对象(或披萨!),从而获得更大的灵活性并减少错误。
以下是我观察到的几点,它们说明了构建器模式如何比其他方法(如伸缩构造函数或设置器)更具优势:
1). 简化多参数对象的构造
- 当对象具有许多可选或强制参数时,使用标准构造函数可能会变得混乱且容易出错。
- 建造者模式提供了一种清晰的、循序渐进的对象创建方法,可以更轻松地仅设置必要的属性,而无需担心参数顺序。
示例:而不是这个令人困惑的构造函数:
const car = new Car('Tesla', 'Model S', 2024, 'Red', true, false);
您将获得以下可读代码:
const car = new CarBuilder('Tesla', 'Model S')
.setYear(2024)
.setColor('Red')
.addGPS()
.build();
2). 支持流畅接口(方法链)
- 流畅接口允许方法返回构建器对象本身,从而实现方法链。这使得代码更加直观易读,因为它提供了一个合乎逻辑的、循序渐进的属性设置过程。
优点:每个方法调用读起来就像是构造对象的一系列指令,提高了可读性。
3). 优雅地处理可选参数
- 使用构造函数时,如果不传递空值或创建许多重载构造函数,则很难处理可选参数。
- 构建器模式允许您仅包含所需的参数,而不必担心为每个参数提供默认值或占位符。
不使用 Builder(使用构造函数):
const car = new Car('Tesla', 'Model S', 2024, null, true, false); // Confusing
使用 Builder:
const car = new CarBuilder('Tesla', 'Model S')
.setYear(2024)
.addGPS()
.build(); // Clean and understandable
4). 促进不变性
- 对象一旦构建完成,通常就不可变了——创建后就无法更改其状态。这确保了构造的对象处于有效且稳定的状态,从而使代码更安全,更不容易出错。
优点:调用build()方法后,对象已完全构造并有效,减少了由于对象部分构造而导致错误的机会。
5). 更易于扩展
- 建造者模式易于扩展。添加新属性或功能时,可以修改建造者,而不会影响现有代码。不使用建造者模式时:如果向构造函数添加新参数,则必须在整个代码库中更改该对象的每次实例化。
使用 Builder:您只需向构建器添加一个新的 setter 方法,而不会影响构建对象的位置。
示例:添加引擎类型:
const car = new CarBuilder('Tesla', 'Model S')
.setYear(2024)
.setColor('Red')
.setEngineType('Electric')
.build();
6). 提高可读性和可维护性
- 建造者模式通过将构建过程分解为小的、易于理解的步骤,提供了很高的可读性。
- 这使得代码更具自文档性——您可以清楚地看到对象是如何构造的,而无需查找构造函数中每个参数代表什么。优点:由于每个方法都清楚地定义了它正在修改对象的哪个部分,因此代码更易于维护和修改。
7). 防止对象不一致
- Builder 确保在构建对象之前设置所有必需的参数,从而防止创建不一致或不完整的对象。
- 此验证过程发生在构建时,确保对象在创建时处于有效状态。
如果没有 Builder:您可能会创建不完整或初始化不正确的对象。
使用 Builder:构建器模式保证对象的一致性和完全构造性。
8). 减少对重载构造函数的需求
- 如果没有建造者模式,你最终可能需要重载多个构造函数来适应不同的参数组合。优点:有了建造者模式,你不需要多个版本的构造函数,只需要一个清晰、灵活的构建流程。
让我们使用一个真实的例子来比较使用和不使用建造者模式的对象创建,在这个例子中,我们正在创建一个具有多个属性的 Car 对象,其中一些属性是可选的。
不使用建造者模式
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; // Optional parameter
this.hasSunroof = hasSunroof || false; // Optional parameter
}
describe() {
return `${this.year} ${this.make} ${this.model} in ${this.color} color with GPS: ${this.hasGPS} and Sunroof: ${this.hasSunroof}`;
}
}
// Creating the object
const car = new Car('Tesla', 'Model S', 2024, 'Red', true, false);
console.log(car.describe());
使用建造者模式
2). 使用建造者模式
// Car class remains simple, no complex constructor
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} in ${this.color} color with GPS: ${this.hasGPS} and Sunroof: ${this.hasSunroof}`;
}
}
// CarBuilder class for step-by-step object creation
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); // Builds and returns the Car object
}
}
// Creating the object using the builder
const car = new CarBuilder('Tesla', 'Model S')
.setYear(2024)
.setColor('Red')
.addGPS()
.build();
console.log(car.describe());
我很想听听你是如何将这些想法运用到工作中的?请在下方评论区分享你的想法或疑问——我期待你的反馈。
感谢您与我一起踏上这次学习之旅!
文章来源:https://dev.to/srishtikprasad/builder-design-pattern-3a7j