重构转换语句,转换语句是否真正有用?


28

我正在阅读本文,并想知道,是否通过用Dictionary或Factory替换它们来摆脱所有switch语句,以便我的项目中根本没有switch语句。

有些东西加起来还不够。

问题是,switch语句是否有任何实际用途,还是我们继续将其替换为字典或工厂方法(当然,在使用工厂方法时,将最少使用switch语句来创建对象)使用工厂...但仅此而已)。


10
假设您实施工厂,您将如何决定要创建哪种类型的对象?
CodeART 2012年


@CodeWorks:当然,我会在某些地方有条件,决定要使用的具体实现。
卡尼尼2012年

@CodeWorks:很明显,使用虚拟构造函数。(而且,如果您不能以这种方式实现工厂模式,则需要一种更好的语言。)
Mason Wheeler

Answers:


44

双方switch陈述和多态性有其用途。请注意,尽管也存在第三个选项(在支持函数指针/ lambda和更高阶函数的语言中):将有问题的标识符映射到处理函数。例如,这在非OO语言的C语言中,以及C *是*,但在Java也是OO *中尚不可用。

在一些过程语言(不具有多态性也不高阶函数)switch/ if-else声明是为了解决一类问题的唯一方法。因此,许多习惯了这种思维方式的开发人员switch甚至继续使用面向对象语言(OO语言),因为多态通常是更好的解决方案。这就是为什么通常建议避免/重构switch支持多态的语句的原因。

无论如何,最佳解决方案始终取决于大小写。问题是:从长远来看,哪个选项可以为您提供更简洁,更简洁,更可维护的代码?

Switch语句经常会变得笨拙,包含数十种情况,从而使维护变得很困难。由于必须将它们保留在一个函数中,因此该函数可能会变得非常庞大。如果是这种情况,则应考虑将其重构为基于地图和/或多态的解决方案。

如果相同的代码switch开始在多个地方弹出,那么多态可能是统一所有这些情况并简化代码的最佳选择。尤其是如果预计将来会增加更多案件,则更是如此。每次需要更新的位置越多,出错的可能性就越大。但是,通常情况下,各个案例处理程序是如此简单,或者它们如此之多,或者它们是如此相互关联,以至于将它们重构为完整的多态类层次结构是过大的,或者导致大量重复代码和/或纠结,很难维护类的层次结构。在这种情况下,使用功能/ lambdas可能会更简单(如果您的语言允许)。

但是,如果您switch在一个地方有一个地方,并且只有少数情况做简单的事情,那么将其保持原样可能是最好的解决方案。

* 我在这里宽松地使用“ OO”一词;我对什么是“真实”或“纯” OO的概念性辩论不感兴趣。


4
+1。同样,这些建议往往用措辞来表达,“ 宁愿选择多态来切换”,这也是一个很好的选择,非常符合该答案。它承认在某些情况下负责任的编码人员可能会做出其他选择。
卡尔·马纳斯特

1
有时候,即使您的代码中有大量的内容,您仍希望使用切换方法。我想到了生成3D迷宫的一段代码。如果将数组中的一字节单元替换为类,则内存使用量将上升。
洛伦·佩希特尔

14

这是我回到恐龙的地方。

Switch语句本身并不是一件坏事,这是由它们所引起的。

最明显的一个是在您的代码中反复多次重复的“相同” switch语句,该语句很糟糕(在那儿这样做,会尽一切努力避免再次出现)-并且这后一种情况您可能能够处理使用多态。嵌套的案例通常也有一些可怕的地方(我曾经有一个绝对的怪物-除了“更好”之外,现在还不确定我现在该如何处理)。

字典作为开关我觉得更具挑战性-从根本上讲,如果您的开关涵盖了100%的情况,但是如果您希望使用默认或不采取任何行动,则开始变得更加有趣。

我认为这是一个避免重复并确保我们在正确的位置组成对象图的问题。

但是,还有一个领悟性(可维护性),并且这两种方法都可以实现-一旦您了解了它的全部工作原理(模式及其实现所在的应用程序)就很容易...但是,如果您来到一行代码中,必须添加一些新内容,然后必须跳到各处来确定需要添加/更改的内容。

尽管我们所有的开发环境都具有强大的功能,但我仍然希望能够理解打印在纸上的代码(就好像它是被打印出来的那样)-您能用手指跟随代码吗?我接受实际上没有,因为今天有很多好的实践,并且出于充分的原因,您不能这样做,但这意味着难以跟上代码的步伐(或者也许我才刚刚老...)


4
我有一位老师说,理想情况下,应该编写代码,以便“对编程一窍不通的人(例如您的母亲)可以理解它”。不幸的是,这是一个理想的选择,但是也许我们还是应该在代码审查中加入我们的妈妈们!
Michael K'5

为“ +1”表示“但是,如果您进入一行代码,必须在其中添加新内容,那么您就必须跳到整个位置来确定需要添加/更改的内容”
quick_now 2012年

8

转换语句与子类型多态性是一个老问题,在FP社区中有关表达问题的讨论中经常提到。

基本上,我们有类型(类)和函数(方法)。我们如何编码东西,以便在后面轻松添加新类型或新方法?

如果您以OO风格编程,则很难添加新方法(因为这将意味着重构所有现有类),但是添加使用与以前相同方法的新类非常容易。

另一方面,如果您使用switch语句(或其等效的OO,观察者模式),则添加新功能非常容易,但是添加新案例/类则非常困难。

要在两个方向上都具有良好的可扩展性并不容易,因此在编写代码时,请根据您更可能在哪个方向上扩展来确定是使用多态还是切换语句。


4
我们通过用Dictionary或Factory替换它们来摆脱所有switch语句,以便我的项目中根本没有switch语句。

不。这样的绝对值很少是一个好主意。

在许多地方,字典/查找/工厂/多态调度将比switch语句提供更好的设计,但是您仍然必须填充字典。在某些情况下,这将掩盖实际发生的事情,并且简单地内联switch语句更易于读取和维护。


确实,如果您展开工厂,字典和查找,它实际上基本上是一个switch语句:如果array [hash(search)]然后调用array [hash(search)]。尽管哪些编译开关不是运行时可扩展的。
Zan Lynx 2012年

@ZanLynx,至少在C#中完全相反。如果查看为非平凡的switch语句生成的IL,则会看到编译器将其转换为字典,因为它更快。
David Arno

@DavidArno:“完全相反”?我的阅读方式完全相同。怎么可能相反呢?
Zan Lynx

2

看起来这是与语言无关的,穿透式代码最适用于以下switch语句:

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

与等效if,默认情况非常尴尬。并记住,越深入,条件列表将越长:

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(但是,考虑到其他一些答案,我觉得我错过了问题的重点...)


我认为在使用穿透语句switch的程度与相同goto。如果要执行的算法需要适合于结构化编程构造的控制流,则应使用此类构造,但是如果算法不适合此类构造,则使用goto可能比尝试添加标志或其他控制逻辑来适合其他控制结构更好。 。
超级猫

1

区分大小写的switch语句确实有用。函数式编程语言使用一种称为模式匹配的方法,该方法可让您根据输入来不同地定义函数。但是,在面向对象的语言中,可以通过使用多态来更好地达到相同的目标。您只需调用该方法,然后根据对象的实际类型,将执行该方法的相应实现。这就是该想法背后的原因,即switch语句具有代码味道。但是,即使使用OO语言,您也可能会发现它们对于实现抽象工厂模式很有用。

tl; dr-它们很有用,但很多时候您可以做得更好。


2
我在那里闻到一些偏见-为什么多态性比模式匹配更优雅?是什么让您认为FP中不存在多态性?
tdammers

@tdammers首先,我并不是要暗示FP中不存在多态性。Haskell支持即席和参数多态。模式匹配对于代数数据类型有意义。但是对于外部模块,这需要公开构造函数。显然,这破坏了抽象数据类型(用代数数据类型实现)的封装。这就是为什么我觉得多态性更优雅(在FP中可能)。
围巾岭

您会通过类型类和实例忘记多态性。
tdammers

您是否听说过表情问题?如果您停止考虑,OO和FP在不同情况下会更好。
hugomg

@tdammers当我提到临时多态性时,您认为我在谈论哪种多态性?Ad Hoc多态性是通过类型类在Haskell中实现的。怎么说我忘了 根本不是真的。
围巾岭
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.