什么是失控开关声明的最佳替代方案?

我inheritance了一个包含一些巨大的switch语句块的项目,其中一些包含多达20个案例。 重写这些的好方法是什么?

你为什么要在不同的结构中重写它们? 如果您确实需要单独处理20个案例,那么就可以使用开关/箱子了。 维持一个if / then逻辑的大链条是可怕的。

如果使用面向对象语言,多态性是另一种选择。 每个子类都将在方法中实现它自己的function。

多态性。 但这可能不是一个微不足道的重构。

一些例子和参考:

重构(Googe书籍)

切换语句代码气味和多态性

重构switch语句

正如其他人所指出的,它取决于switch语句。 但是,在过去,我通过以下方式继续重构switch语句。 假设我们有一个像这样的switch语句,有很多重复的代码

switch(x){ case 1: makeitso("foo"); globalLog += "foo"; case 2: makeitso("bar"); globalLog += "bar"; case 3: makeitso("baz"); globalLog += "baz"; ... default: throw("input error"); } 

首先要做的是识别常见的部件(在现实世界中,这可能会更加实质)

 makeitso([some string]); globalLog += [some string]; 

并把它变成一个函数

 function transformInput(somestring) { makeitso(somestring); globalLog += somestring; } 

然后对于在每种情况下改变的部分,使用散列或数组;

 var transformvalues = ["foo", "bar", "baz"]; 

从这里我们可以这样做:

 var tvals = ["foo", "bar", "baz" ... ]; function transformInput(somestring) { makeitso(somestring); globalLog += somestring; } var tval = tvals[x]; if(tval!==undefined) { transformInput(tval); } else { throw ("invalid input"); } 

从switch语句中考虑了tvals,它甚至可以在外部提供,以扩展您可以处理的案例数量。 或者你可以动态构建它。 但在现实世界中,switch语句通常会有特殊情况。 我把它作为读者的练习。

三个建议(回应已经给出的一些):

  1. 也许开关没有您想象的那么糟糕。 如果案件块很大,那可能会很难看。 通过将逻辑提取到方法中来缩短它们。

  2. 正如许多人所指出的那样,在OO语言中,多态性可能就是答案。

  3. 在函数式语言中,如Javascript,编写一个函数,返回运行任何输入所需的函数。 这可能使用switch语句本身,或者它可能使用查找表。

您始终可以使用查找表 。

在switch语句中有20个案例没有错。 您可以通过重构整理代码,并至少将案例处理移动到方法/函数中。

根据switch语句的评估内容,您可能希望使用策略模式对其进行重构。 请查看这篇文章,了解使用单独的类替换枚举值上的开关来处理每个函数的示例。

也可能是使用具有20个案例的开关实际上可能是它的最佳行动方案。 只要它是可读的,并且每个结果清楚地传达了动作是什么,就没有必要真正重构它。

一般来说,我认为你应该只在需要时进行重构,例如当你想要添加更多function时,但是当前的设计不适用于任务。 然后,您应该重构而不添加新function,然后才添加新function。

在其他情况下,不要为重构而烦恼。 在需要时做,否则可能有更重要的事情要做。

如果你真的需要,那么访问者设计模式是一个常见的交换机案例替换,但你应该注意它确实有缺点。 (请查看www.objectmentor.com/resources/articles/acv.pdf )

这取决于switch语句正在做什么。

如果它匹配字符或字符串,比如在解析器中,并且您没有在代码中的任何位置重复相同的模式集,那么switch语句可能没问题。

如果它与允许值列表匹配(比方说)整数,则可以为每个值创建基类和一组派生类。 然后,无论何时生成整数数据,都可以创建派生类的实例,而不是所有的switch语句“answers”。

第三种选择是创建将模式映射到动作的数据结构(即,使用虚方法的函数或对象)。 您可以在此数据结构中查找切换值,并执行相应的操作。

如果它的工作没有重大错误(不是主要的,我的意思是他们不会让你把你的头发拉出来)为什么还要重构呢? 不要重构一切。

如果你愿意,你可以将它改为多态,但这将是一个猎枪手术,你可能不得不重构比这个开关块更多的东西。

访问https://github.com/Pedram-Ahmadpour/Switch-Case

客户端

创建Condition的实例,然后通过Switch()函数将条件传递给Condition对象。

  int sense = 2; ConditionSense conditionSense = new ConditionSense(); conditionSense.Switch(sense); 

服务器端

  1. 创建条件操作 。 该接口定义了如何执行Condition

     public interface IAction { void Do(); } 
  2. 创建所需的案例列表。 这些类必须实现条件操作ICase ; 让它们保持清淡。

      public class CaseCry : IAction, ICase { public int? Key { get { return 2; } } public void Do() { Sense.Cry cry = new Sense.Cry(); cry.Act(); } } 
    • ICase只保存一个KeySwitch()函数使用它来导航案例。

       public interface ICase { TCase Key { get; } } 
  3. 创建一个它inheritanceSwitchCase通用抽象类的Condition类。

    • 将所有你想要的案例添加到Cases属性中。
    • 定义一个Switch()函数并导航Cases属性以查找匹配情况,然后将它们作为条件操作执行。

       public class ConditionSense : SwitchCase { public ConditionSense() { Cases = new List> { new CaseSmile(), new CaseCry() }; DefaultCases = new List> { new CaseNoSense() }; } public void Switch(int? key) { IEnumerable matches = Cases.Where(p => p.Key.Equals(key)) .Select(p => p as IAction); if (matches.Count() > 0) foreach (IAction match in matches) match.Do(); else foreach (IAction defaultCase in DefaultCases) defaultCase.Do(); } } 

微笑哭泣 ……,可以是巨大的,不要担心它们的大小; 条件动作ICase让它们保持懒惰负载。

  public class Sense { public class Smile { public void Act() { Console.WriteLine("I'm smiling :-)"); } } public class Cry { public void Act() { Console.WriteLine("I'm crying :-("); } } public class NoSense { public void Act() { Console.WriteLine("I've no sense :-|"); } } }