使用ES6类继承进行参数化验证的值

时间:2016-06-12 16:18:09

标签: javascript validation oop inheritance ecmascript-6

我正在重写一些旧的Chrome扩展程序代码,同时尝试学习新的ES6技巧,而且我遇到了一些设计问题。

我的目标是提供一个值存储(由异步chrome.storage支持持久性,但这超出了问题的范围)。我想要的是将一些验证与值相关联。因此,我的StorageValues的集合,每个都与验证功能相关联。

在我的旧版本中,我只是在实例化一个值时传递验证函数,类似这样(简化):

Storage["key1"] = new Value({
  validator: ValidatorIsInteger, defaultValue: 0, /* ... */
});
Storage["key2"] = new Value({
  validator: ValidatorEnum(["a", "b", "c"]), defaultValue: "a", /* ... */
});

但是,我正在尝试用Value作为class重写,可以使用特定验证器进行扩展,这在当时似乎是一个好主意。再次,简化:

class Value {
  constructor(key, defaultValue) {
    this.key = key;
    this.defaultValue = defaultValue;
    this.set(defaultValue);
  }

  set(newValue) {
    var validationResult = this.validate(newValue);
    if (validationResult.pass) {
      this.value = newValue;
      return newValue;
    } else {
      throw new RangeError(
        `Value ${newValue} for ${this.key} failed validation: ${validationResult.message}`
      );
    }
  }

  get() { return this.value; }

  // Overload in children
  validate(value) {
    return {pass: true};
  }
}

class IntegerValue extends Value {
  validate(value) {
    if (Number.isInteger(value)) {
      return {pass: true};
    } else {
      return {pass: false, message: "Value must be an integer"};
    }
  }
}

到目前为止一切顺利。但是,在尝试创建参数化子类时,我遇到了problems

class EnumValue extends Value {
  constructor(key, defaultValue, possibleValues) {
    this.possibleValues = possibleValues; // NUH-UH, can't set that before super()
    super(key, defaultValue);
  }

  // Will be called from parent constructor
  validate(value) {
    if (this.possibleValues.includes(value)) {
      return {pass: true};
    } else {
      return {pass: false, message: `Value must be in [${this.possibleValues}]`};
    }
  }
}

问题在于在调用.set(defaultValue)之前“设置”参数化验证器。我看到了几种方法,所有这些似乎都缺乏:

  • 辞职,而不是使用class - 基于扩展程序的方法 - 我想看看它是否可以先修复
  • 始终信任默认值作为调用.set(defaultValue) - 错误的变通方法,因为我不希望数据意外不一致
  • 使.set()异步,让构造函数在执行验证之前有机会完成 - ,而持久性后端是异步的,Storage的目的是提供一个同步“缓存”

我没有看到这种方法有一些明显的解决方法吗?如果没有,这对于这项工作来说只是一个错误的工具,我该如何重新组织呢?

1 个答案:

答案 0 :(得分:3)

这是从构造函数调用可重写方法(validate,来自set)的经典问题; discussed here(不同的语言,相同的问题)。

您的特定示例适用于几种解决方法,但一般问题仍然存在。

要解决一般问题,我会直接设置value,而不是通过set,并使用单元测试来确保我没有创建具有无效默认值的验证器。毕竟,这是编码错误,而不是运行时错误。

但如果您想继续致电set,可以选择以下几种方式:

  • 您可以拥有Value的概念具有没有默认值,无论如何这可能是有用的。这将通过让您拥有一个不期望接收默认值的Value构造函数来解决问题。您可以在构建后通过Value或类似内容提供setDefaultValue默认值;该方法将验证,但这很好,因为它将被称为子类中的后构造。

  • 您可以为Value提供“验证”和“非验证”状态,并让构造函数接受一个标志,指示它应该从哪个状态开始。子类将使用false如果他们有特殊的验证行为,请确保他们的所有鸭子都在一行中,然后设置验证状态(这将验证)。