适用于大型层次结构的Javascript ES6通用构建器模式

时间:2018-01-04 12:56:09

标签: javascript inheritance design-patterns ecmascript-6 builder-pattern

我正在尝试为Javascript中的大型对象层次结构提供通用构建器函数。目标是在层次结构的顶部实现尽可能多的功能。在玩了一会儿之后,我最终得到了一个我喜欢的结构,虽然我并不完全满意。

结构目前看起来有点像这样。我在下面附上了一个工作(简化)版本:

enter image description here

class AbstractItem {

  constructor(build) {
    if (this.constructor === AbstractItem) {
      throw new TypeError("Oops! AbstractItem should not be instantiated!");
    }
    this._id = build.id;
  }

  /*
  	The generic ItemBuilder is part of an abstract superclass.
    Every item should have an ID, thus the builder validates this.
    It also provides a generic build() function, so it does not have to be re-implemented by every subclass.
  */
  static get Builder() {

		/*
    	This constant references the constructor of the class for which the Builder function was called.
      So, if it was called for ConcreteItem, it will reference the constructor of ConcreteItem.
      This allows us to define a generic build() function.
    */
    const BuildTarget = this;

    class ItemBuilder {

      constructor(id) {      
        if (!id) {
          throw new TypeError('An item should always have an id!');
        }        
        this._id = id;
      }
			
			//The generic build method calls the constructor function stored in the BuildTarget variable and passes the builder to it.
      build() {
        return new BuildTarget(this);
      }

      get id() {
        return this._id;
      }
    }

    return ItemBuilder;
  }

  doSomething() {
    throw new TypeError("Oops! doSomething() has not been implemented!");
  }

  get id() {
    return this._id;
  }
}

class AbstractSubItem extends AbstractItem {

  constructor(build) {  
    super(build);    
    if (this.constructor === AbstractSubItem) {
      throw new TypeError("Oops! AbstractSubItem should not be instantiated!");
    }    
    this._name = build.name;
  }

	/*
  	AbstractSubItem implements a different version of the Builder that also requires a name parameter.
  */
  static get Builder() {

		/*
    	This builder inherits from the builder used by AbstractItem by calling the Builder getter function and thus retrieving the constructor.
    */
    class SubItemBuilder extends super.Builder {

      constructor(id, name) {
        super(id);
        if (!name) {
          throw new TypeError('A subitem should always have a name!');
        }
        this._name = name;
      }

      get name() {
        return this._name;
      }
    }

    return SubItemBuilder;
  }

  get name() {
    return this._name;
  }
}

class ConcreteItem extends AbstractItem {

  doSomething() {
    console.log('Hello world! My name is ' + this.id + '.');
  }
}

class ConcreteSubItem extends AbstractSubItem {

  doSomething() {
    console.log('Hello world! My name is ' + this.name + ' (id: ' + this.id + ').');
  }
}

new ConcreteItem.Builder(1).build().doSomething();
new ConcreteSubItem.Builder(1, 'John').build().doSomething();

在我看来,我目前的做法有一些利弊。

赞成

  • Builder()方法提供了一个通用接口,可用于获取所有实现类的构建器。
  • 我的具体类可以继承构建器类而无需任何额外的努力。
  • 使用继承,可以根据需要轻松扩展构建器。
  • 构建器代码是抽象类的一部分,因此很清楚在阅读代码时构建的是什么。
  • 调用代码易于阅读。

缺点

  • 目前尚不清楚,查看Builder()getter函数,需要哪些参数来避免异常。了解这一点的唯一方法是查看构造函数(或注释中),这些构造函数深埋了几层。
  • 如果SubItemBuilder继承自super.Builder而不是顶级类,那会让人感到反直觉。同样,在不查看SubItemBuilder示例的情况下,其他如何从ItemBuilder继承可能并不清楚。
  • 查看AbstractItem类并不是很清楚它应该使用构建器构建。

有没有办法改进我的代码来否定我提到的一些缺点?任何反馈都将非常感激。

0 个答案:

没有答案