【设计模式】创建型模式:生成器模式

225 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

生成器模式

分步骤创建复杂对象,使得可以使用相同的创建代码生成不同的对象。(将变化部分的代码放在生成器中)

适用场景

1.在可选参数较多时

示例问题:现在需要建造一栋房屋,可以是简单房屋,也可以是豪华的房屋。 如果是通过一个包括所有可能参数的超级构造函数来创建房屋对象,那么对于简单房屋而言,构造函数中绝大部分的参数都没有使用,会使得对于构造函数的调用十分不简洁。

通过超级构造函数建造房子

在支持方法重载的编程语言中,可以新建几个只有较少参数的简化版构造函数,但这些函数仍需调用最复杂的构造函数,传递默认值来替代省略的参数。

“重叠构造函数”

这时可以通过使用生成器模式将对象构造代码从产品类中抽取出来,并将其放在一个名为生成器的独立对象中。 无需调用所有步骤,而只需调用创建特定对象配置所需的那些步骤即可。

房屋生成器

2.在需要创建不同形式的产品时

可以创建多个不同的生成器, 用不同方式实现一组相同的创建步骤。(通过生成器隔离变化与不变的代码) 如下图:同一建造步骤,通过生成器使用不同的建造材料产生不同房子。

同一建造步骤产生不同产品

3.在对象组成部分不确定时

在某些情况下,一个对象的组成部分不是一开始就可以确定的,需要分步骤进行:一边解析一边创建。 以下是 AWTK 加载 UI 界面的代码,通过一边解析二进制文件,一边通过生成器生成 UI 界面中的控件,并设置其控件属性。

ret_t ui_loader_load_default(ui_loader_t* loader, const uint8_t* data, uint32_t size,
                             ui_builder_t* b) {
  rbuffer_t rbuffer;
  widget_desc_t desc;
  uint32_t magic = 0;
  uint8_t widget_end_mark = 0;

  return_value_if_fail(loader != NULL && data != NULL && b != NULL, RET_BAD_PARAMS);
  return_value_if_fail(rbuffer_init(&rbuffer, data, size) != NULL, RET_BAD_PARAMS);
  return_value_if_fail(rbuffer_read_uint32(&rbuffer, &magic) == RET_OK, RET_BAD_PARAMS);
  return_value_if_fail(magic == UI_DATA_MAGIC, RET_BAD_PARAMS);

  ui_builder_on_start(b);
  while ((rbuffer.cursor + sizeof(desc)) <= rbuffer.capacity) {
    const char* key = NULL;
    const char* value = NULL;
    return_value_if_fail(rbuffer_read_binary(&rbuffer, &desc, sizeof(desc)) == RET_OK,
                         RET_BAD_PARAMS);
    ui_builder_on_widget_start(b, &desc);

    return_value_if_fail(rbuffer_read_string(&rbuffer, &key) == RET_OK, RET_BAD_PARAMS);
    while (*key) {
      return_value_if_fail(rbuffer_read_string(&rbuffer, &value) == RET_OK, RET_BAD_PARAMS);
      ui_builder_on_widget_prop(b, key, value);
      return_value_if_fail(rbuffer_read_string(&rbuffer, &key) == RET_OK, RET_BAD_PARAMS);
    }
    ui_builder_on_widget_prop_end(b);

    if (rbuffer_has_more(&rbuffer)) {
      return_value_if_fail(rbuffer_peek_uint8(&rbuffer, &widget_end_mark) == RET_OK,
                           RET_BAD_PARAMS);
      while (widget_end_mark == 0) {
        rbuffer_read_uint8(&rbuffer, &widget_end_mark);
        ui_builder_on_widget_end(b);
        if ((rbuffer.cursor + 1) >= rbuffer.capacity ||
            rbuffer_peek_uint8(&rbuffer, &widget_end_mark) != RET_OK) {
          break;
        }
      }
    }
    if (b->widget == NULL) break;
  }
  ui_builder_on_end(b);

  return RET_OK;
}

总结

优点

  • 可以简化构造函数,避免 “重叠构造函数” 的出现。
  • 生成不同形式的产品时, 可以复用相同的制造代码。
  • 可以分步创建对象,适用于对象组成部分不确定,需要边解析边创建。
  • 单一职责原则,可以将每种产品生成代码抽取到同一位置,使得代码易于维护。

缺点

  • 代码整体复杂程度会有所增加。

参考

22种设计模式:refactoringguru.cn/design-patterns

AWTK:github.com/zlgopen/awtk

《设计模式:可复用面向对象软件的基础》