【发布时间】:2021-09-20 09:02:08
【问题描述】:
考虑一下误用密封类,我们看看下面的设计。
有两个模块:
-
parser(Kotlin) - 负责从 String 中创建实例 -
processor(Java) - 将原始传入数据泵入强类型存储(即关系表)
- 字符串来自外部源到
processor -
processor将其认可委托给parser -
parser基于一些rules X创建不同类型的实例[Banana, Nail, Shoe] -
processor基于一些rules Y将每个实例持久化到适当的表中
在parser 中使用这样的密封类是否合适,然后在processor 中根据每个实例的具体类型做出决定?
// parser module exposes Item and its subclasses
sealed interface Item {
class Banana(/*state 1*/) : Item
class Nail(/*state 2*/) : Item
class Shoe(/*state 3*/) : Item
}
fun parse(value: String, rule: ParseRule): Item {
return when (true) {
rule.canParseBanana(value) -> rule.makeBananaFrom(value)
rule.canParseNail(value) -> rule.makeNailFrom(value)
rule.canParseShoe(value) -> rule.makeShoeFrom(value)
else -> throw RuntimeException("cannot parse")
}
}
// processor module makes decisions based on class
void process(String value){
Item item = parser.parse(value);
if (item instance of Item.Banana){
persistBanana((Item.Banana) item)
} else if ( ... )
// etc
} else {
throw new RuntimeException("Unknown subclass of Item : " + item.getClass())
}
}
我发现这种方法有问题,因为越来越多的 Item 子类可能会导致设计灾难,但无法弄清楚是否存在与这个完全不同的密封类的“规范”用例。
什么是密封类适用性的限制,当系统设计者应该更喜欢“较少类型”的东西时,如下所示:
class Item{
Object marker; // String or Enum
Map<String, Object> attributes;
}
// basically it is the same, but without dancing with types
void process(String value){
Item item = parser.parse(value);
if ("BANANA".equals(item.marker)){
persistBanana(item.attributes)
} else if (...){
// etc
}
}
【问题讨论】:
-
我相信您提出的问题适用于 Kotlin 和 Java。您应该始终尝试坚持正确的 OOP,因此重载/实现方法而不是使用 when/switch/if 链。那么有多少子类并不重要。有时,最好使用枚举/密封接口,但通常会发生在可能元素的集合有限且它们在未来不会发生太大变化的情况下。如果
when代码知道所有可能的子类型是合乎逻辑的,因为通常它确实不应该。 -
我很难为您的案例提出具体的解决方案。让项目自己持久化可能是有意义的,例如将函数添加到
Item:fun persist(<whatever is needed to persist>)。另一方面,根据定义,数据库层知道所有实体,因此有时创建这样的 if 链可能是有意义的。我绝对不鼓励像您上一个示例中那样使用字符串。对于这种情况,密封接口或枚举要好得多。 -
从这个例子中删除
sealed关键字并没有改变任何东西,这似乎回答了这里是否合适的问题。它没有增加任何价值。这里真正的问题似乎根本与sealed无关。而是 OOP 中最基本的问题:如何用不同的具体实现对抽象进行建模。 -
@jaco0646 你是对的。我猜你比我更能表达我的想法。事实上,问题是关于密封类的限制。我故意滥用它们(因为它们的移除不会影响设计)。问题是我看不到何时应用密封类是合适的(或者可能是不可避免的)。看着答案,我发现我并不孤单。
-
你有没有读过一些关于密封类的热门话题,例如Why seal a class? 和When and why would you seal a class? 很多语言都有这个功能,所以这个问题或多或少与语言无关。
标签: java kotlin design-patterns sealed-class