Javascript ES6类固有参数化验证值

Javascript ES6类固有参数化验证值,javascript,validation,oop,inheritance,ecmascript-6,Javascript,Validation,Oop,Inheritance,Ecmascript 6,我正在重写一些旧的Chrome扩展代码,同时尝试学习新的ES6技巧,我遇到了一些设计问题 我的目标是提供一个值存储(由异步chrome.storage支持持久性,但这超出了问题的范围)。我想要的是将一些验证与值关联起来。因此,我的存储是值的集合,每个值都与验证函数关联 在我的旧版本中,当我实例化一个值时,我只会传递一个验证函数,类似这样(简化): 然而,我正试图用值作为一个类重写它,这个类可以通过特定的验证器进行扩展,这在当时似乎是个好主意。同样,简化: Storage["key1"] = ne

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

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

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

然而,我正试图用
作为一个
重写它,这个类可以通过特定的验证器进行扩展,这在当时似乎是个好主意。同样,简化:

Storage["key1"] = new Value({
  validator: ValidatorIsInteger, defaultValue: 0, /* ... */
});
Storage["key2"] = new Value({
  validator: ValidatorEnum(["a", "b", "c"]), defaultValue: "a", /* ... */
});
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"};
    }
  }
}
到目前为止还不错。但是,我在尝试创建参数化子类时遇到:

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)
之前“设置”参数化验证器。我认为有几种方法可以解决这个问题,但所有这些方法似乎都不具备:

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

我是否没有看到这种方法的一些明显的修正?如果不是,并且这对于工作来说只是一个错误的工具,我应该如何重新组织它?

这是从构造函数调用可重写方法(
validate
,via
set
)的典型问题;(不同的语言,相同的问题)

您的具体示例有几个变通方法,但一般问题仍然存在

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

但如果您想继续调用
set
,有几个选项:

  • 您可以使用
    值的概念,该值没有默认值,这在一般情况下可能很有用。这将通过让您拥有一个不希望接收默认值的
    构造函数来解决问题。您可以通过
    setDefaultValue
    或类似方式,在构建后为
    提供默认值;该方法将进行验证,但这很好,因为它将在子类中被称为后期构造

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

感谢您提供的“经典问题”链接,我几乎没有OOP经验,因此对标准gotchas的解释非常有用。