我正在重写一些旧的 Chrome 扩展代码,同时尝试学习新的 ES6 技巧,并且遇到了一些设计问题。
我的目标是提供一个值存储(由异步chrome.storage
支持以实现持久性,但这超出了问题的范围)。我想要的是将一些验证与值相关联。因此,我的 Storage
是 Values
的集合,每个值都与一个验证函数相关联。
在我的旧版本中,当我实例化一个值时,我只会传递一个验证函数,如下所示(简化):
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()
异步,让构造函数有机会在执行验证之前完成 - 虽然持久化后端是异步的,但存储
的目的其中之一就是提供同步“缓存”。
我是否没有看到这种方法的一些明显的修复方法?如果不是,而且这根本就是一个错误的工作工具,我应该如何重新组织它?
最佳答案
这是从构造函数调用可重写方法(validate
,通过set
)的经典问题; discussed here (不同的语言,相同的问题)。
您的具体示例提供了几种解决方法,但一般问题仍然存在。
为了解决一般问题,我直接设置 value
,而不是通过 set
,并使用单元测试来确保我没有创建具有以下属性的验证器:无效的默认值。毕竟,这是一个编码错误,而不是运行时错误。
但是如果您想继续调用set
,有几个选项:
您可能有一个没有默认值的
值
概念,无论如何,这通常可能很有用。这可以通过让您拥有一个不期望接收默认值的Value
构造函数来解决问题。您可以在构造后通过setDefaultValue
或类似方法为Value
指定默认值;该方法会验证,但这没关系,因为它将在子类中被称为构造后。您可以给
Value
一个“验证”和“非验证”状态,并让构造函数接受一个标志,表示它应该开始的状态。子类将使用false
如果他们有特殊的验证行为,请确保所有鸭子都排成一行,然后设置验证状态(将进行验证)。
关于javascript - 使用 ES6 类继承进行参数化验证的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37776479/