11

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

4

11 回答 11

16

为什么要以不同的结构重写它们?如果你真的有 20 个案例需要单独处理,那么 switch/case 就是要走的路。一大串 if/then 逻辑很难维护。

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

于 2009-01-04T06:06:57.207 回答
6

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

一些例子和参考:

重构(谷歌书籍)

Switch 语句代码异味和多态性

重构 switch 语句

于 2009-01-04T06:05:08.893 回答
5

正如其他人指出的那样,这取决于 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");
} 

并且将 tvals 排除在 switch 语句之外,甚至可以在外部提供它以扩展您可以处理的案例数量。或者您可以动态构建它。然而,在现实世界中,switch 语句通常会有特殊情况。我把它作为练习留给读者。

于 2009-01-04T11:36:58.100 回答
4

三个建议(与已经给出的一些建议相呼应):

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

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

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

于 2009-01-04T11:51:02.173 回答
2

一般来说,我认为你应该只在需要时进行重构,例如当你想添加更多功能时,但当前的设计不能胜任这项任务。然后,您应该在不添加新功能的情况下进行重构,然后才添加新功能。

在其他情况下,不要费心重构。当你需要的时候去做,否则可能有更重要的事情要做。

如果你真的需要,那么访问者设计模式是一种常见的 switch-case 替换,尽管你应该注意到它确实有缺点。(即,查看www.objectmentor.com/resources/articles/acv.pdf

于 2009-01-04T11:40:35.503 回答
2

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

于 2009-01-04T06:10:30.100 回答
2

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

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

于 2009-01-04T06:15:18.513 回答
2

您总是可以使用查找表

于 2009-01-04T06:06:13.773 回答
0

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

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

如果它匹配(例如)一个整数与允许值列表,您可以为每个值创建一个基类和一组派生类。然后,首先生成整数数据的任何东西都可以使用所有 switch 语句“答案”创建派生类的实例。

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

于 2009-01-04T06:15:15.520 回答
0

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

如果您愿意,您可以将其更改为多态,但这将是一次霰弹枪手术,您可能需要重构的不仅仅是这个开关块。

于 2009-01-04T07:58:40.583 回答
-1

访问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<int?>
        {
            public int? Key { get { return 2; } }
    
            public void Do()
            {
                Sense.Cry cry = new Sense.Cry();
                cry.Act();
            }
        }
    
    • ICase只保存一个KeySwitch()函数使用它来导航案例。

      public interface ICase<TCase>
      {
          TCase Key { get; }
      }
      
  3. 创建一个继承SwitchCase通用抽象类的Condition类。

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

      public class ConditionSense : SwitchCase<int?>
      {
          public ConditionSense()
          {
              Cases = new List<ICase<int?>>
              {
                  new CaseSmile(),
                  new CaseCry()
              };
      
              DefaultCases = new List<ICase<int?>> {
                  new CaseNoSense()
              };
          }
      
          public void Switch(int? key)
          {
              IEnumerable<IAction> 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 :-|");
                }
            }
        }
于 2017-03-11T11:47:35.933 回答