我们是否应该总是偏爱多态性而不是枚举?

23

看完视频《Clean Code Talks -- 继承、多态和测试》后,我检查了我的代码,发现有几个switch语句可以重构成多态,但我也注意到我只在枚举类型上使用了switch语句。这是否意味着枚举在面向对象设计中是“邪恶”的,应该用多态来消除它们?

6个回答

13
首先,Java实际上具有可用于多态的枚举类。我不是Java程序员,所以其他人肯定可以给出一个好的例子(我不能)。此外,请注意,多态通常只是一种过度设计。我也看过这个演讲,它很棒,但它只提供了指导方针,并没有银弹。
特别需要注意的是,并非所有switch语句都可以替换为枚举类。状态机正是一个适合使用枚举类的案例。在解析中,我经常使用状态机。当然,这可以通过设计模式和类多态性来完成。但代码量更多,执行同样的工作,速度更慢,可读性并没有提高,而且这种解决方案仅在一个单独的地方需要,没有任何代码重用。在这里使用子类化根本没有优势。
然而,这是一个例外情况。一般来说,在支持良好的语言中,子类化通常是更好的解决方案。
编辑:我注意到这可能引起争议。当然,有许多很好的解决方案可以封装解析,从正则表达式到诸如Antlr之类的解析器生成框架。没有问题,在除了微不足道的情况下这些都是更好的解决方案。然而,我经常使用低级代码(显然不是Java),在那里不支持正则表达式,而解析器生成器也会产生额外的开销。

1
第一次看到“不要使用IF语句”的Google演讲时,我有一半的感觉是“这是一个好主意”,另一半则是“这只是过于极端的宣传”。 - BobbyShaftoe

13

枚举本身并不是邪恶的,邪恶的是switch语句。在C++ FAQ Book中有一个长时间的讨论,但要点是:除了有限的领域 --- 例如从设备寄存器中读取数据的解释 --- 一个大的switch语句意味着您正在使用数据来区分子类型。相反,您应该只使用子类型,以便获得编译器的帮助来保持其正确性,并且这也意味着编译器将在您(不可避免地)更改情况集时自动添加新的情况。


11
class Sunday extends DayOfWeek {}
class Monday extends DayOfWeek {}
class Tuesday extends DayOfWeek {}
class Wednesday extends DayOfWeek {}
class Thursday extends DayOfWeek {}
class Friday extends DayOfWeek {}
class Saturday extends DayOfWeek {}

枚举类型很好。


如果我想对它们进行排序怎么办?是的,你列出的例子很极端,但除了你列出的方法和枚举之外还有其他方法:结构体DayOfWeek {string Name; int Order;}。 - TheCatWhisperer
此外,一旦您需要本地化您的应用程序,您的枚举将有多大帮助? - TheCatWhisperer
@TheCatWhisperer:非常有用。例如在C#中:var day = DayOfWeek.Tuesday; var localized = CultureInfo.CurrentCulture.DateTimeFormat.DayNames[(int)day]; - recursive

7

我不太愿意称任何东西为“邪恶”。这是一个问题,涉及到“你想要多么重的工程”。

枚举/开关在某些领域是可以的。设置类层次结构是不总是需要的开销。但是,如果 case 语句中的代码越多,那么你可能需要采用更加复杂的方法。

我的经典经验是几年前我为班级编写的编译器。我的室友和我上了同样的课,但我们采用了两种非常不同的方法。我采取了一种重度面向对象的方法,充满了多态性。他采用了一种重度 C 语言方法,使用枚举和联合。他的代码行数大约是我的一半,编译速度更快,而且他的代码能够正常工作。他的代码很灵活,因为它没有被过度设计。这对我来说是软件设计方面的宝贵经验。


没错。一旦你走上这条路,就会产生很多开销。突然间,为了增加一个额外的情况,你不得不建立一个完整的类,而不是一个额外的标志和一点代码。 - BobbyShaftoe
2
是的,您必须创建一个新类,但核心代码逻辑应该保持相同数量,否则您就做错了什么。 - Pyrolistical
编码的复杂性增加了小型开关的不受欢迎程度。 - Paul Nathan

3

这是否意味着在面向对象设计中,枚举类型是“有害”的,应该用多态性来替代?

通常情况下是这样的。

switch/enum结构可以是许多多态结构之一:状态(State)和策略(Strategy)是最常见的两种。


1

我认为枚举在数值已知且数量不多的情况下非常有用。
此外,枚举以一种命名常量的方式命名。

枚举没有存储除其值之外的任何其他状态。
当您有不同条件状态(并且条件需要比单个变量依赖更多状态)时,多态将非常有帮助。


在Java中,枚举类型不仅仅是一堆常量。 - PhiLho
这个问题并没有提到Java/C#。而且,我也没有说这是Java中的情况。 - shahkalpesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接