【发布时间】:2022-01-01 01:17:19
【问题描述】:
我有一个 Value 接口,它带有一个将值显示为字符串的方法。 通常值是一个整数,所以 IntegerValue 实现了 Value。 有时值是未知的,我使用空对象模式,所以 UnknownValue 实现了 Value。
当该值实际上是一个整数时,客户端检查该值是否足够高(IntegerValue.isEnough)很有用。这会影响该值稍后向用户显示的方式。但是,如果该值未知,则检查它是否足够高是没有意义的——该值是未知的。根据接口隔离原则,UnknownValue 不应该有isEnough 方法。
interface Value {
toString(): string;
}
class IntegerValue implements Value {
private value: number;
constructor(v: number) { this.value = v }
isEnough() { return this.value >= 30 }
toString() { return '' + this.value }
}
class UnknownValue implements Value {
toString() { return 'unknown' }
}
但是客户端访问Value 并且不知道它是否是IntegerValue。所以我必须检查然后进行类型转换。
if(value.toString() !== 'unknown') {
handleInteger(value as IntegerValue) // <-- check if isEnough inside
} else {
handleUnknown(value)
}
我想知道是否有一种设计模式可以通过多态来解决这个问题,而无需类型转换。
我正在考虑这样的访问者模式:
interface ValueVisitor {
handleInteger(v: IntegerValue): void;
handleUnknown(): void
}
class ViewValueVisitor implements ValueVisitor { ... }
class JsonSerializerValueVisitor implements ValueVisitor { ... }
interface Value {
toString(): string;
acceptVisitor(v: ValueVisitor): void;
}
class IntegerValue implements Value {
...
acceptVisitor(v) { v.handleInteger(this) }
}
class UnknownValue implements Value {
...
acceptVisitor(v) { v.handleUnknown() }
}
但是访问者模式违反了开放封闭原则。我想知道是否有更好的解决方案。
【问题讨论】:
-
我不确定如何避免踩到您关心的设计模式/原则。在 TS 中,约定是使用union type 之类的
type SomeValue = IntegerValue | UnknownValue,然后进行一些区分测试,例如value instanceof UnknownValue或"isEnough" in value(example)。更常规的是,您会将其设为明确的discriminated union,例如this。这些是否满足您的需求? -
我也在考虑使用联合类型,但这也违反了客户端代码中的开放封闭原则。 (如果我想添加一个 StringValue 类,我必须通过添加新的 if 语句来更改客户端代码)。有许多几乎没有问题的解决方案,但我想借此机会探索经典的 OOP 原则,以便在处理更复杂的情况时变得更加通用。不过,我没有意识到受歧视的工会会这样工作。谢谢你教我。
-
如果您添加了一个客户端不知道的新类,并且
Value接口中没有可让您对它进行任何操作的通用功能,那么我不知道可能有任何解决方案。如果客户知道如何handleInteger()和handleUnknown()就是这样,他们可以做this。不过,我看到你已经从这个问题的标签中删除了 TypeScript,所以我现在要离开了,因为我的 TypeScript 特定知识可能不是特别有用。祝你好运!
标签: oop design-patterns interface casting polymorphism