建造者模式(生成器模式) Builder

概述

建造者模式属于创建型模式。顾名思义,builder 的意思是建造者或者建筑工人。例如,楼房都是千差万别,楼房的外形,层数,内部房间的数量,房间的装饰都不一样。但是建筑一座楼房,抽象出来的建筑流程是确定的,都可以归纳为几个步骤:打桩、建地基、搭框架、内部建设等。同理,建造者设计模式也是基于这样的概念而生的,这个设计模式用来解决什么样的情况呢:面对 "一个复杂对象" 的创建,其流程不变,但每个流程实现的具体细节是会变化的。

建造者模式概念是指将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。嗨,这个概念看起来总是这么深奥难懂。大概的意思,同一套构建流程,但是基于不同的构建流程细节,创建出了不同的产品(表示)。

运用

角色

  • Builder : 一个抽象接口,规范子建造者对于生产的产品的各个组成部分的建造。
  • ConcreteBuilder : 子建造者,实现 Builder 类。不同的子建造者提供了不同的流程细节。
  • Director : 指挥者类,为建造者制定同一套构建流程。
  • Product : 产品类,一般是一个比较复杂的对象。

实例

一软件公司欲开发一个音乐播放软件,为了给用户使用提供方便,该播放软件提供多种界面显示模式,如完整模式、精简模式、记忆模式、网络模式等。在不同的显示模式下主界面的组成元素有所差异,如在完整模式下将显示菜单、播放列表、主窗口、控制条等,在精简模式下只显示主窗口和控制条,而在记忆模式下将显示主窗口、控制条、收藏列表等。

product 类

class MusicPlayer {

    constructor(){
        this._type = '';
        this._menu = '';
        this._playerList = '';
        this._mainWindows = '';
        this._controlStrip = '';
        this._collectionList = '';
    }
    get type(){
        return this._type; 
    }
    set type(type){
        this._type = type;
    }

    // 省略其他 get 和 set 方法
}

Builder 类

class PlayerBuilder {
    constructor() {
        this._player = new MusicPlayer();
    }
    buildType(){
        throw new Error("This method must be overwritten!");
    }
    buildMenu(){
        throw new Error("This method must be overwritten!");
    }
    buildPlayerList(){
        throw new Error("This method must be overwritten!");
    }
    buildMainWindows(){
        throw new Error("This method must be overwritten!");
    }
    buildControlStrip(){
        throw new Error("This method must be overwritten!");
    }
    buildCollectionList(){
        throw new Error("This method must be overwritten!");
    }
    createPlayer(){
        return this._player;
    }
}

ConcreteBuilder 具体的建造者类

// 完整模式
class FullPlayerBuilder extends PlayerBuilder{

    buildType() {
        this._player.type = "完整模式";
    }
    buildMenu() {
        this._player.menu = "菜单";
    }
    buildPlayerList() {
        this._player.playerList = "播放列表";
    }
    buildMainWindows() {
        this._player.mainWindows = "主界面";
    }
    buildControlStrip() {
        this._player.controlStrip = "控制条";
    }
    buildCollectionList() {
        this._player.collectionList = "收藏列表";
    }
}

// 精简模式
class SimplePlayerBuilder extends PlayerBuilder{

    buildType() {
        this._player.type = "精简模式";
    }
    buildMenu() {
        this._player.menu = null;
    }
    buildPlayerList() {
        this._player.playerList = null;
    }
    buildMainWindows() {
        this._player.mainWindows = "主界面";
    }
    buildControlStrip() {
        this._player.controlStrip = "控制条";
    }
    buildCollectionList() {
        this._player.collectionList = null;
    }
}

// 记忆模式
class MemoryPlayerBuilder extends PlayerBuilder{

    buildType() {
        this._player.type = "记忆模式";
    }
    buildMenu() {
        this._player.menu = null;
    }
    buildPlayerList() {
        this._player.playerList = null;
    }
    buildMainWindows() {
        this._player.mainWindows = "主界面";
    }
    buildControlStrip() {
        this._player.controlStrip = "控制条";
    }
    buildCollectionList() {
        this._player.collectionList = "收藏列表";
    }
}

Director 类

class PlayerController {
    construct( pb ) {
        pb.buildType();
        pb.buildMenu();
        pb.buildPlayerList();
        pb.buildMainWindows();
        pb.buildControlStrip();
        pb.buildCollectionList();
        return pb.createPlayer();
    }
}

测试一下

let builder = new SimplePlayerBuilder();
let controller = new PlayerController();
let player = controller.construct(builder);
console.log( player );

建造者模式优缺点

优点

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

缺点

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

results matching ""

    No results matching ""