11

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

如果我运行代码指标(在解决方案资源管理器中右键单击项目名称并选择“计算代码指标” - 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的可维护性指数

(当然,如果你只有这个,这是微不足道的,但如果你使用像类这样的实用程序,其哲学正在做这样的事情,你的实用程序类的可维护性指数将非常糟糕..)

这有什么解决办法?

4

7 回答 7

26

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

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

可维护性指数基于三个代码指标:即 Halstead Volumen、Cyclomatic Complexity 和 Lines of Code,并基于以下公式

MAX(0,(171 - 5.2 * ln(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * ln(Lines of Code))*100 / 171)

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

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

想象一下代码有一个用于几天(周一至周日)的开关块加上一个用于几个月(1 月至 12 月)的开关块。此代码将非常易读和可维护,但会导致巨大的圈复杂性,因此在 Visual Studio 2010 中的可维护性指数非常低。

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

于 2010-06-21T13:01:29.257 回答
5

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

正确的解决方案(上面提到的 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,您应该以这种方式结束),因为它很容易模拟。

于 2011-10-28T13:59:01.550 回答
3

我知道这个答案已经很晚了,但我很感兴趣没有人提出字典解决方案。我发现在处理像这样面向数据的巨大 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)分开。

于 2014-05-28T19:37:48.763 回答
2

有两件事浮现在脑海:

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

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
    }
}
于 2010-05-29T22:43:17.860 回答
2

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

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();
}

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

于 2010-05-29T23:19:11.920 回答
0

我不知道这有多重要,但以下得到 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];
}
于 2010-05-29T23:04:31.927 回答
0

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

于 2010-09-23T13:38:55.667 回答