我正在重写一些旧的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()
异步,让构造函数在执行验证之前有机会完成 - ,而持久性后端是异步的,Storage
的目的是提供一个同步“缓存”。我没有看到这种方法有一些明显的解决方法吗?如果没有,这对于这项工作来说只是一个错误的工具,我该如何重新组织呢?
答案 0 :(得分:3)
这是从构造函数调用可重写方法(validate
,来自set
)的经典问题; discussed here(不同的语言,相同的问题)。
您的特定示例适用于几种解决方法,但一般问题仍然存在。
要解决一般问题,我会直接设置value
,而不是通过set
,并使用单元测试来确保我没有创建具有无效默认值的验证器。毕竟,这是编码错误,而不是运行时错误。
但如果您想继续致电set
,可以选择以下几种方式:
您可以拥有Value
的概念具有没有默认值,无论如何这可能是有用的。这将通过让您拥有一个不期望接收默认值的Value
构造函数来解决问题。您可以在构建后通过Value
或类似内容提供setDefaultValue
默认值;该方法将验证,但这很好,因为它将被称为子类中的后构造。
您可以为Value
提供“验证”和“非验证”状态,并让构造函数接受一个标志,指示它应该从哪个状态开始。子类将使用false
如果他们有特殊的验证行为,请确保他们的所有鸭子都在一行中,然后设置验证状态(这将验证)。