探索创建型设计模式,作为一个高级前端的你,不用进来了!

635 阅读7分钟

前言

建造者模式可以将一个复杂的对象分解为多个简单的部分,并提供一个独立的Builder类来构建这些部分。Builder类通常包含一个构建方法,它会将多个部分组装成一个完整的对象。这个模式的主要思想是将对象的创建和表示分离开来。

Builder类通常有一个与之关联的产品类。产品类定义了要创建的对象的属性和方法。Builder类实现了一个创建产品对象的接口,并提供了一个构建方法来组装产品对象。Builder类还可以提供其他方法,以便设置或修改产品对象的属性。

Director类负责使用Builder类来构建对象。它通常有一个构建方法,它将Builder类的构建方法作为参数,并使用它来创建对象。Director类可以进一步封装Builder类,以简化客户端的使用。

建造者模式的优点是它可以让我们创建不同的表示,同时保持相同的构建过程。这使得代码更加可维护,因为我们可以将构建逻辑与表示逻辑分离开来。它还可以减少代码重复,因为我们可以使用相同的构建方法来构建不同的表示。

建造者模式的缺点是它会增加代码的复杂度。我们需要创建多个类来实现该模式,并且构建过程可能会变得更加复杂。此外,建造者模式可能不适合简单的对象创建。

建造者模式的应用

建造者模式通常用于创建复杂的对象。以下是一些建造者模式的应用:

游戏开发。建造者模式可以用另一个与建造者模式经常一起使用的设计模式是工厂方法模式。工厂方法模式与建造者模式的不同之处在于,工厂方法模式将对象的创建委托给子类,而建造者模式将对象的创建委托给Builder类。

以下是一个简单的游戏开发场景中,使用了建造者模式和工厂方法模式的代码示例

// Product类:游戏角色类
class GameCharacter {
  constructor() {
    this.hp = 0;
    this.attack = 0;
    this.defense = 0;
    this.speed = 0;
  }
}

// Builder类:游戏角色的建造者,负责构建角色属性
class GameCharacterBuilder {
  constructor() {
    this.character = new GameCharacter();
  }

  // 设置角色血量
  setHp(hp) {
    this.character.hp = hp;
    return this;
  }

  // 设置角色攻击力
  setAttack(attack) {
    this.character.attack = attack;
    return this;
  }

  // 设置角色防御力
  setDefense(defense) {
    this.character.defense = defense;
    return this;
  }

  // 设置角色速度
  setSpeed(speed) {
    this.character.speed = speed;
    return this;
  }

  // 获取最终建造好的角色
  getResult() {
    return this.character;
  }
}

// Factory类:游戏角色的工厂,根据角色类型生产角色
class GameCharacterFactory {
  constructor() {
    this.builder = new GameCharacterBuilder();
  }

  // 生产战士角色
  createWarrior() {
    return this.builder.setHp(100).setAttack(50).setDefense(30).setSpeed(30).getResult();
  }

  // 生产法师角色
  createMage() {
    return this.builder.setHp(60).setAttack(80).setDefense(20).setSpeed(40).getResult();
  }
}

// 客户端代码:创建一个战士和一个法师
const factory = new GameCharacterFactory();
const warrior = factory.createWarrior();
const mage = factory.createMage();

// 打印出创建的角色信息
console.log(warrior);
console.log(mage);

建造者模式可以与抽象工厂模式合作。在这种情况下,抽象工厂模式负责创建一组相关对象的实例,而建造者模式负责创建这些实例所需的组件。例如,一个汽车制造工厂可能使用一个抽象工厂来创建发动机、轮胎和其他零部件的实例,而使用一个建造者来组装这些部件以创建一辆完整的汽车。

下面是一个结合建造者模式和抽象工厂模式的代码示例,用于创建一辆汽车。

首先,我们定义一个抽象工厂,用于创建汽车的各个部件(发动机、轮胎、座椅等):

// 抽象工厂
class CarPartFactory {
  createEngine() {}
  createWheel() {}
  createSeat() {}
}

然后,我们实现具体的工厂,用于创建具体的部件实例:

// 具体工厂1
class CarPartFactory1 extends CarPartFactory {
  createEngine() {
    return new Engine1();
  }
  createWheel() {
    return new Wheel1();
  }
  createSeat() {
    return new Seat1();
  }
}

// 具体工厂2
class CarPartFactory2 extends CarPartFactory {
  createEngine() {
    return new Engine2();
  }
  createWheel() {
    return new Wheel2();
  }
  createSeat() {
    return new Seat2();
  }
}

接下来,我们定义一个汽车建造者,用于组装汽车部件:

// 建造者
class CarBuilder {
  constructor(factory) {
    this.factory = factory;
  }

  build() {
    const car = new Car();
    car.engine = this.factory.createEngine();
    car.wheels = [
      this.factory.createWheel(),
      this.factory.createWheel(),
      this.factory.createWheel(),
      this.factory.createWheel(),
    ];
    car.seats = [
      this.factory.createSeat(),
      this.factory.createSeat(),
      this.factory.createSeat(),
      this.factory.createSeat(),
    ];
    return car;
  }
}

最后,我们定义具体的汽车类:

// 具体汽车
class Car {
  constructor() {
    this.engine = null;
    this.wheels = [];
    this.seats = [];
  }

  displayInfo() {
    console.log(`Engine: ${this.engine.name}`);
    console.log(`Wheels: ${this.wheels.map((wheel) => wheel.name).join(", ")}`);
    console.log(`Seats: ${this.seats.map((seat) => seat.name).join(", ")}`);
  }
}

现在,我们可以使用建造者模式和抽象工厂模式来创建不同类型的汽车。例如:

const carBuilder1 = new CarBuilder(new CarPartFactory1());
const car1 = carBuilder1.build();
car1.displayInfo();

const carBuilder2 = new CarBuilder(new CarPartFactory2());
const car2 = carBuilder2.build();
car2.displayInfo();

输出结果分别为:

Engine: Engine1
Wheels: Wheel1, Wheel1, Wheel1, Wheel1
Seats: Seat1, Seat1, Seat1, Seat1

Engine: Engine2
Wheels: Wheel2, Wheel2, Wheel2, Wheel2
Seats: Seat2, Seat2, Seat2, Seat2

这个例子展示了如何结合建造者模式和抽象工厂模式来创建复杂对象。抽象工厂模式负责创建对象的各个部分,而建造者模式则负责组装这些部分以创建完整的对象。

建造者模式相关的模式是原型模式。原型模式是一种创建模式,它允许你复制或克隆一个对象,而无需从头开始创建一个新对象。建造者模式可以与原型模式一起使用,以便在已有的对象上创建新的变体。例如,一个文档编辑器可以使用原型模式创建一个初始文档对象,然后使用建造者模式在该对象的基础上创建新的文档变体。

下面提供一个结合建造者模式和原型模式的示例,假设我们正在开发一个游戏,游戏中有不同的角色,每个角色都有自己的属性和技能,我们使用原型模式创建初始的角色,然后使用建造者模式在该角色的基础上创建新的变体。

首先,我们创建一个角色的原型对象:

const RolePrototype = {
  name: "default",
  type: "default",
  level: 1,
  skills: [],
  clone: function() {
    return Object.create(this);
  }
};

上述代码中,我们定义了一个名为RolePrototype的对象,它包含了角色的基本属性,其中的clone方法可以用来创建一个新的角色对象。

接下来,我们创建一个建造者类RoleBuilder,用来创建新的角色:

class RoleBuilder {
  constructor() {
    this.role = RolePrototype.clone();
  }

  setName(name) {
    this.role.name = name;
    return this;
  }

  setType(type) {
    this.role.type = type;
    return this;
  }

  setLevel(level) {
    this.role.level = level;
    return this;
  }

  addSkill(skill) {
    this.role.skills.push(skill);
    return this;
  }

  build() {
    return this.role;
  }
}

上述代码中,我们定义了一个RoleBuilder类,它包含了一系列的方法用来设置角色的各个属性,并最终创建一个新的角色对象。

最后,我们可以使用这两个类来创建新的角色对象,如下所示:

// 先创建一个原型角色
const prototypeRole = RolePrototype.clone();
prototypeRole.name = "protagonist";
prototypeRole.type = "warrior";
prototypeRole.level = 10;
prototypeRole.skills = ["swordsmanship", "shield defense"];

// 使用建造者创建一个新的角色对象
const newRole = new RoleBuilder()
  .setName("villain")
  .setType("mage")
  .setLevel(5)
  .addSkill("fireball")
  .build();

console.log(prototypeRole); // { name: 'protagonist', type: 'warrior', level: 10, skills: [ 'swordsmanship', 'shield defense' ], clone: [Function: clone] }
console.log(newRole); // { name: 'villain', type: 'mage', level: 5, skills: [ 'fireball' ] }

上述代码中,我们首先使用RolePrototype创建了一个初始的角色对象prototypeRole,然后使用RoleBuilder创建了一个新的角色对象newRole,在创建过程中使用了不同的属性设置方法,最终创建了一个新的角色对象。

通过上述示例可以看出,建造者模式和原型模式的结合使用,可以更加灵活和高效地创建新的对象。建造者模式可以提供一系列的构造步骤,用来创建新对象的不同属性和特征,而原型模式可以提供一个初始对象,用来作为新对象

结合了建造者模式、工厂方法模式和装饰者模式电子商务网站的示例:

假设我们正在开发一个电子商务网站,需要实现商品管理功能。我们先定义一个 Product 类表示商品,并实现一些基本的属性和方法:

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }

  getDetails() {
    return `Product: ${this.name}, Price: ${this.price}`;
  }
}

然后我们需要实现一个工厂方法来创建 Product 对象:

class ProductFactory {
  createProduct(name, price) {
    return new Product(name, price);
  }
}

接下来,我们使用建造者模式来创建一个复杂的商品对象,包含多个属性和方法。我们定义一个 ProductBuilder 类来构建商品对象:

class ProductBuilder {
  constructor(name, price) {
    this.product = new Product(name, price);
  }

  setCategory(category) {
    this.product.category = category;
    return this;
  }

  setBrand(brand) {
    this.product.brand = brand;
    return this;
  }

  setDescription(description) {
    this.product.description = description;
    return this;
  }

  setFeatures(features) {
    this.product.features = features;
    return this;
  }

  build() {
    return this.product;
  }
}

ProductBuilder 类中,我们通过链式调用的方式设置商品的各个属性,并最终返回完整的商品对象。

接下来,我们使用装饰者模式对商品对象进行装饰,添加一些额外的功能,比如打折、促销等。我们定义一个 ProductDecorator 类来实现装饰者:

class ProductDecorator {
  constructor(product) {
    this.product = product;
  }

  getPrice() {
    return this.product.price;
  }

  setPrice(price) {
    this.product.price = price;
  }

  getDetails() {
    return this.product.getDetails();
  }
}

然后我们定义一个 DiscountDecorator 类,来实现打折功能:

class DiscountDecorator extends ProductDecorator {
  constructor(product, discountRate) {
    super(product);
    this.discountRate = discountRate;
  }

  getPrice() {
    return this.product.price * (1 - this.discountRate);
  }

  getDetails() {
    return `${super.getDetails()}, Discount Rate: ${this.discountRate}`;
  }
}

最后,我们可以在客户端代码中使用这些类来创建和装饰商品对象:

const productFactory = new ProductFactory();

const productBuilder = new ProductBuilder("手机", 999);
const product = productBuilder
  .setCategory("zzzzzzzz")
  .setBrand("xxxx")
  .setDescription("xxx")
  .setFeatures(["xx", "xxxxxxxxxxxxx"])
  .build();

const discountProduct = new DiscountDecorator(product, 0.2);
discountProduct.setPrice(799);

console.log(product.getDetails());
console.log(discountProduct.getDetails()); 

缺点

  1. 需要创建多个类:实现建造者模式需要创建多个类,包括产品类、抽象建造者类、具体建造者类和指挥者类等。这增加了代码量和复杂度。
  2. 需要一定的了解和经验:使用建造者模式需要一定的了解和经验,尤其是在创建复杂对象时更为明显,否则可能会出现设计上的问题。
  3. 不适合创建简单对象:建造者模式适用于创建复杂对象,如果需要创建简单对象,使用建造者模式反而会增加代码量和复杂度。
  4. 可能会产生多余的对象:由于需要创建多个类来实现建造者模式,可能会导致产生一些不必要的对象,增加了系统的负担。

优点

  1. 分离对象的创建和使用,使得系统更加灵活:建造者模式将对象的创建过程与使用过程分离,使得两者可以独立变化。这样一来,可以让建造者模式更加灵活,能够适应不同的需求和变化。
  2. 避免重复的对象创建代码:通过使用建造者模式,可以避免重复的对象创建代码。建造者模式将对象的创建过程封装在一个独立的Builder类中,这样就可以在不同的地方重复使用该类,从而避免了重复的对象创建代码。
  3. 可以控制对象的创建过程:通过建造者模式,可以精确地控制对象的创建过程。建造者模式将对象的创建过程分解成多个步骤,并且允许用户自定义每个步骤的具体实现。这样就可以精确地控制对象的创建过程,从而得到期望的结果。
  4. 可以创建复杂的对象:建造者模式可以创建复杂的对象,包括那些具有复杂内部结构和大量属性的对象。通过将对象的创建过程分解成多个步骤,并且允许用户自定义每个步骤的具体实现,可以有效地创建复杂的对象。
  5. 可以提高代码的复用性:通过使用建造者模式,可以提高代码的复用性。由于建造者模式将对象的创建过程封装在一个独立的Builder类中,因此可以在不同的地方重复使用该类,从而提高代码的复用性。

总结:

建造者模式是一种创建模式,它通过将对象的创建委托给Builder类来创建复杂的对象。这种模式通常适用于需要创建多个具有相似组成部分的对象。与其他创建模式如工厂方法模式和抽象工厂模式相比,建造者模式更注重组装对象,而不是简单地创建它们。因此,建造者模式在创建复杂对象时更具优势。

建造者模式的优点包括可以逐步构建复杂对象、将对象创建和表示分离、将对象组装的过程可定制化、以及更好地控制对象的创建过程。建造者模式的缺点包括增加了代码复杂性、可能导致产生过多的Builder类、以及对象的组装顺序可能影响创建过程。

建造者模式可以与其他模式如工厂方法模式、抽象工厂模式和原型模式一起使用,以实现更复杂的功能。在实践中,建造者模式被广泛应用于创建复杂的对象,例如GUI构建器、文档编辑器和游戏引擎等领域。