【问题标题】:Visual Studio Code Metrics and the Maintainability index of switch caseVisual Studio Code Metrics 和 switch case 的可维护性指标
【发布时间】:2011-02-25 13:55:59
【问题描述】:

作为一个喜欢遵循最佳实践的人,

如果我在以下位置运行代码度量(在解决方案资源管理器中右键单击项目名称并选择“计算代码度量” - Visual Studio 2010):

    public static string GetFormFactor(int number)
    {
        string formFactor = string.Empty;
        switch (number)
        {
            case 1:
                formFactor = "Other";
                break;

            case 2:
                formFactor = "SIP";
                break;

            case 3:
                formFactor = "DIP";
                break;

            case 4:
                formFactor = "ZIP";
                break;

            case 5:
                formFactor = "SOJ";
                break;
        }

        return formFactor;
    }

它给了我一个61的可维护性指数

(当然,如果你只有这个,这是微不足道的,但如果你使用像类这样的实用程序,其哲学是做这样的事情,你的实用程序类的可维护性指数会差得多..)

解决办法是什么?

【问题讨论】:

    标签: visual-studio indexing maintainability code-metrics


    【解决方案1】:

    首先:61 被认为是可维护的代码。对可维护性指数来说,100 是非常容易维护的代码,而 0 是很难维护的。

    • 0-9 = 难以维护
    • 10-19 = 适度维持
    • 20-100 = 好维护

    可维护性指数基于三个代码指标:即 Halstead Volumen、循环复杂度和代码行数,并基于 following formula

    MAX(0,(171 - 5.2 * ln(霍尔斯特德 体积) - 0.23 *(循环 复杂度)- 16.2 * ln(线数 代码))*100 / 171)

    事实上,在您的示例中,可维护性指数低值的根本原因是循环复杂性。该指标是根据代码中的各种执行路径计算得出的。不幸的是,该指标不一定符合代码的“人类可读性”。

    示例代码导致索引值非常低(请记住,较低意味着更难维护),但实际上它们很容易阅读。这在使用 Cyclomatic Complexity 评估代码时很常见。

    想象一下代码有一个用于几天(Mon-Sun)的开关块加上一个用于几个月(Jan-Dec)的开关块。这段代码非常可读和可维护,但会导致巨大的圈复杂度,因此在 Visual Studio 2010 中的可维护性指数非常低。

    这是关于度量的众所周知的事实,如果根据数字来判断代码,则应该考虑这一点。与其查看绝对数字,不如随着时间的推移对这些数字进行监控,以将其理解为代码更改的指标。例如。如果可维护性指数始终为 100,然后突然下降到 10,则应检查导致此问题的更改。

    【讨论】:

    【解决方案2】:

    我知道这个答案已经很晚了,但我很感兴趣没有人提出字典解决方案。我发现在处理像这样面向数据的大型 switch 语句时,将 switch-case 折叠到字典中通常更具可读性。

    public static readonly IDictionary<int, string> Factors = new Dictionary<int, string> {
       { 1, "Other" },
       { 2, "SIP" },
       { 3, "DIP" },
       { 4, "ZIP" },
       { 5, "SOJ" }
    };
    
    public static string GetFormFactor2(int number)
    {
       string formFactor = string.Empty;
       if (Factors.ContainsKey(number)) formFactor = Factors[number];
       return formFactor;
    }
    

    这为您提供了 74 的可维护性指数 - 略低于数组解决方案,因为类与字典耦合,但我觉得它更通用,因为它适用于您通常会打开的任意数量的类型。和数组解决方案一样,它的扩展性很好,并且去除了很多重复的代码。

    一般来说,使用数据驱动的方法可以帮助您的代码更清晰,因为它将重要部分(在本例中为条件和结果)与粗略部分(在本例中为 switch-case)分开.

    【讨论】:

      【解决方案3】:

      显然,Enum 方法对我来说是最易于维护的,因为它不涉及硬编码字符串,因此没有拼写问题和编译时语法检查。 只有限制是命名规则。

      【讨论】:

        【解决方案4】:

        可维护性指数可能会更高,因为您为解决方案选择的方法缺乏可扩展性。

        正确的解决方案(上面提到的 Mark Simpson)是可以扩展以使用新形式因素而无需重新构建代码的解决方案 - 代码中的 switch/case 语句始终表明 OO 设计已被遗忘,应该总是被视为糟糕的代码气味。

        就个人而言,我会实现...

        interface IFormFactor
        {
            // some methods
        }
        
        class SipFormFactor : IFormFactor...
        
        class DipFormFactor : IFormFactor...
        
        Etc.
        

        ...并让接口上的方法提供所需的功能 - 你可以认为它[我猜]类似于 GoF 命令模式。

        这样你的更高级别的方法就可以......

        MyMethod(IFormFactor factor)
        {
            // some logic...
        
            factor.DoSomething();
        
            // some more logic...
        }
        

        ...您可以在以后加入并引入新的外形尺寸,而无需像使用硬编码的 switch 子句那样更改此代码。您还会发现这种方法也很容易适用于 TDD(如果您正确地执行 TDD,您应该以这种方式结束),因为它很容易模拟。

        【讨论】:

          【解决方案5】:

          我会这样做并忘记可维护性指数:

          public static string GetFormFactor(int number)
          {
              switch (number)
              {
                  case 1: return "Other";
                  case 2: return "SIP";
                  case 3: return "DIP";
                  case 4: return "ZIP";
                  case 5: return "SOJ";
              }
          
              return number.ToString();
          }
          

          恕我直言,易于阅读且易于更改。

          【讨论】:

          • 我就是这样做的.. 但我真的很想知道是否有其他解决方案
          • 我发现下面提到的数组和枚举方法的可读性和可维护性较差。
          【解决方案6】:

          我不知道这有多重要,但以下得到 76:

          private static string[] _formFactors = new[] { null, "Other","SIP","DIP", "ZIP", "SOJ"};
          public static string GetFormFactor2(int number)
          {
              if (number < 1 || number > _formFactors.Length)
              {
                  throw new ArgumentOutOfRangeException("number");
              }
          
              return _formFactors[number];
          }
          

          【讨论】:

          • 是的,它是一个解决方案,但不是很优雅:P 我正在以一种结构方式思考这样做。有什么想法吗?
          【解决方案7】:

          我想到了两件事:

          使用枚举来结合描述和价值

          public enum FormFactor
          {
              Other = 1,
              SIP = 2,
              etc.
          }
          

          使用类或结构来表示每种形式因素

          public class FormFactor 
          {
              public int Index { get; private set; }
              public string Description { get; private set; }
          
              public FormFactor(int index, string description)
              {
                  // do validation and assign properties
              }
          }
          

          【讨论】:

          • 嗯,我很难理解您的解决方案。你能再具体一点吗?更详细的例子?问候
          猜你喜欢
          • 2011-05-13
          • 2010-09-07
          • 2012-07-07
          • 2014-03-23
          • 2010-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多