建造者模式

建造者模式

Builder模式, 中文翻译为建造者模式或者构建者模式, 也有人叫它生成器模式. 也是比较常用的创建型设计模式.

本身代码的原理和实现掌握起来不难, 难点在于他的应用场景.

下面可以围绕这两个问题进行了解

  1. 既然可以直接用构造函数 或者使用set函数设置参数进行构建对象, 为什么还需要使用建造者进行对象的构建
  2. 建造者模式和工厂模式都可以创建对象,那它们两个的区别在哪里

介绍

建造者模式将包含多个部件的对象的构建过程进行剥离, 客户只需要给出他需要的部件类型, 不用关心如何构建, 以及构建的细节.

比如你,我都需要造一间房子, 造房子的过程无非是从打地基, 筑墙, 装修等过程, 但是最后你, 我期望的房子的风格肯定是不一样的, 最终建造的房子肯定呈现不同风格.

问题1. 为什么使用建造者

平常开发中, 最常用new 构造出一个对象.

假设 这里有个一个类 House

#define DEFAULT_ROOF "default roof"
#define DEFAULT_WALL "default wall"
#define DEFAULT_FLOOR "default floor"
class House {
private:
  std::string roof_;
  std::string wall_;
  std::string floor_;
public:
  House(std::string roof, std::string wall, std::string floor) {
    if (roof.empty()) {
      roof = DEFAULT_ROOF;
    }
    if (wall.empty()) {
      wall = DEFAULT_WALL;
    }
    if (floor.empty()) {
      floor = DEFAULT_FLOOR;
    }
    roof_ = roof;
    wall_ = wall;
    floor_ = floor;
  }
};

给空字符串表示使用默认风格的装饰. 现在只有三个选项, 但如果需要增加其他的装修什么吊灯, 沙发啥的… 参数逐渐增多,变成了8个、10个,甚至更多. 如果继续沿用现在的风格, 构造函数只会变得更长, 可读性和易用性就会变差.

这个时候就会想到定义set变量的函数, 你需要用哪个就自己设这个变量

#define DEFAULT_ROOF "default roof"
#define DEFAULT_WALL "default wall"
#define DEFAULT_FLOOR "default floor"

class House {
private:
  std::string roof_;
  std::string wall_;
  std::string floor_;
public:
  House() {}
  void setRoof(std::string roof) { roof_ = roof;}
  void setWall(std::string wall) { wall_ = wall;}
  void setFloor(std::string floor) { floor_ = floor;}
};

到现在仍未使用 建造者模式, 但可以再想下以下场景是否能够适配

  1. 假设有一些必填项, 比如大小, 空间, 占地, 门牌… 这些需要放到构造函数中强制填入, 那参数列表又会很长. 但如果使用set 函数, 没地方进行校验.
  2. 假设大小, 占地这些参数有关联和约束, 也没有地方进行校验.
  3. 假设房子造完了, 我不期望他的属性进行修改, 那就不能暴露他的 set函数

这时候可以使用建造者

我们可以把校验逻辑放置到Builder类中,先创建建造者,并且通过set()方法设置建造者的变量值,然后在使用build()方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象。

当然我们也可以将Builder类设计成独立的非内部类

以下代码隐藏了 house的set 函数, 并在House::Builder::build中进行了校验

#define DEFAULT_ROOF "default roof"
#define DEFAULT_WALL "default wall"
#define DEFAULT_FLOOR "default floor"

class House {
private:
  std::string roof_;
  std::string wall_;
  std::string floor_;
  House() {}
  void setRoof(std::string roof) { roof_ = roof;}
  void setWall(std::string wall) { wall_ = wall;}
  void setFloor(std::string floor) { floor_ = floor;}
public:
  void print()  {
    std::cout << "roof : " << roof_ << std::endl;
    std::cout << "wall : " << wall_ << std::endl;
    std::cout << "floor : " << floor_ << std::endl;
  }
  class Builder {
  private:
    std::string roof_;
    std::string wall_;
    std::string floor_;
  public:
    Builder() {}
    ~Builder() {}
    void setRoof(std::string roof) { roof_ = roof; }
    void setWall(std::string wall) { wall_ = wall; }
    void setFloor(std::string floor) { floor_ = floor; }
    House* build() {
      if (roof_.empty()) {
        roof_ = DEFAULT_ROOF;
      }
      if (wall_.empty()) {
        wall_ = DEFAULT_WALL;
      }
      if (floor_.empty()) {
        floor_ = DEFAULT_FLOOR;
      }
      House *house_ = new House();
      house_->setRoof(roof_);
      house_->setWall(wall_);
      house_->setFloor(floor_);
      return house_;
    }
  };
};

客户使用方式

int main() {
  House::Builder builder;
  builder.setFloor("red floor");
  auto* house = builder.build();
  house->print();
  return 0;
}

效果

./bin/design/Builder
roof : default roof
wall : default wall
floor : red floor

问题2. 与工厂模式有何区别?

工厂模式是用来创建不同但是相关类型的对象, 由给定的参数来决定创建哪种类型的对象. 建造者模式是用来创建一种类型的复杂对象, 通过设置不同的可选参数, “定制化”地创建不同的对象.

网上有一个经典的例子很好地解释了两者的区别.

顾客走进一家餐馆点餐, 我们利用工厂模式, 根据用户不同的选择, 来制作不同的食物, 比如披萨、汉堡、沙拉. 对于披萨来说, 用户又有各种配料可以定制, 比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨.

优点

  1. 将构建过程进行剥离, 客户无需关心建造的细节. 使用同样的建造过程创建不同风格的对象.
  2. 如果将建造者放到类外实现, 可以实现多个建造者建造不同风格的house, 这些建造者相互独立, 无依赖.

缺点

  1. 创建的产品一般具有较多的共同点, 如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

适用

  • 需要生成的产品对象有复杂的内部结构(通常包含多个成员变量);
  • 产品对象内部属性有一定的生成顺序;
  • 同一个创建流程适用于多种不同的产品。
    代码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值