建造者模式

建造者模式Builder Pattern又可以称为生成器模式,是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,建造者模式属于对象创建型模式。

描述

建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

模式结构

  • Builder: 抽象建造者,引入抽象建造者的目的,是将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。
  • ConcreteBuilder: 具体建造者,实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品、返回组建好的产品。
  • Director: 指挥者,负责调用适当的建造者来组建产品,指挥者一般不与产品类发生依赖关系,与指挥者直接交互的是建造者类,一般来说指挥者被用来封装程序中易变的部分。
  • Product: 产品角色,一般是一个较为复杂的对象,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。

比较

  • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
  • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂 ,通过对部件的组装可以返回一辆完整的汽车。

优点

  • 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
  • 可以更加精细地控制产品的创建过程,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合开闭原则。

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

实现

class Product{ // 具体产品
    constructor(){
        this.name = null;
        this.type = null;
    }
    setName(name){
        this.name = name;
    }
    setType(type){
        this.type = type;
    }
    showProduct(){
        console.log("name:", this.name);
        console.log("type:", this.type);
    }
}

class Builder{ // 抽象建造者
    setPart(name, type){
        throw new Error("Abstract method cannot be called");
    }
    getProduct(){
        throw new Error("Abstract method cannot be called");
    }
}

class ConcreteBuilder extends Builder{ // 实体建造者
    constructor(){
        super();
        this.product = new Product();
    }
    build(name, type){
        this.product.setName(name);
        this.product.setType(type);
    }
    getProduct(){
        return this.product;
    }
}

class Director{ // 指挥者
    constructor(){
        this.builder = new ConcreteBuilder();
    }
    getProductA(){
        this.builder.build("A", "TypeA");
        return this.builder.getProduct();
    }

    getProductB(){
        this.builder.build("B", "TypeB");
        return this.builder.getProduct();
    }
}

(function() {
    var director = new Director();
    var productA = director.getProductA();
    productA.showProduct(); // name: A type: TypeA
    var director = new Director();
    var productB = director.getProductB();
    productB.showProduct(); // name: B type: TypeB
})()

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://www.runoob.com/design-pattern/builder-pattern.html https://wiki.jikexueyuan.com/project/java-design-pattern/builder-pattern.html https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/builder.html