Visual Studio Code Metrics 和 switch case 的可维护性指数

发布于 2024-09-03 14:54:46 字数 843 浏览 5 评论 0原文

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

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

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

这个问题的解决方案是什么?

As a person who loves to follow the best practices,

If i run code metrics (right click on project name in solution explorer and select "Calculate Code Metrics" - Visual Studio 2010) on:

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

It Gives me a Maintainability index of 61

(of course this is insignificant if you have only this, but if you use an utility like class whos philosophy is doing stuff like that, your utility class will have the maintainability index much worst..)

Whats the solution for this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

夏见 2024-09-10 14:54:46

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

  • 0-9 = 难以维护
  • 10-19 = 中等维护
  • 20-100 = 良好维护

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

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

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

示例代码会导致索引值非常低(请记住,较低意味着更难维护),但实际上它们非常容易阅读。当使用循环复杂度来评估代码时,这种情况很常见。

想象一下代码,有一个为期几天(周一至周日)的开关块和一个为期几个月(一月至十二月)的开关块。该代码将非常具有可读性和可维护性,但会导致巨大的循环复杂性,从而导致 Visual Studio 2010 中的可维护性指数非常低。

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

First of all: 61 is considered to be maintainable code. For the Maintainability Index, 100 is very easy to maintain code, while 0 is hard to maintain.

  • 0-9 = hard to maintain
  • 10-19 = moderate to maintain
  • 20-100 = good to maintain

The Maintainability Index is based on three code metrics: Namely the Halstead Volumen, the Cyclomatic Complexity and Lines of Code and based on the following formula:

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

In fact, in your example the root cause for the low value of the Maintainability Index is the Cyclomatic Complexity. This metric is calculated based on the various execution paths in the code. Unfortunately, the metric does not necessarily align with "human readability" of code.

Examples as your code result in very low index values (remember, lower means harder to maintain) but in fact they are very easy to read. This is common when using Cyclomatic Complexity to evaluate code.

Imagine code that has a switch-block for days (Mon-Sun) plus a switch-block for Months (Jan-Dec). This code will be very readable and maintainable but will result in a enormous Cyclomatic Complexity and thus in a very low Maintainability Index in Visual Studio 2010.

This is a well known fact about the metric and should be considered if code is judged based on the figures. Rather than looking at the absolute number, the figures should be monitored over time to understand as an indicator for the change of the code. E.g. if the Maintainability Index was always at 100 and suddenly went down to 10 you should inspect the change that caused this.

梦罢 2024-09-10 14:54:46

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

正确的解决方案(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,您应该最终会采用这种方法),因为它很容易进行模拟。

The maintainability index could be higher because of the lack of expandability in the method you are choosing for your solution.

The correct solution (Mark Simpson touched on above) is the one that can be expanded to use a new form factor without the code being rebuilt - switch / case statements in code are always a sign that OO design has been forgotten and should always be seen as a bad code smell.

Personally, I would implement...

interface IFormFactor
{
    // some methods
}

class SipFormFactor : IFormFactor...

class DipFormFactor : IFormFactor...

Etc.

...and have the methods on the interface provide the desired functionality - you can think of it [I guess] as being similar in to GoF Command Pattern.

This way your higher level methods can just be...

MyMethod(IFormFactor factor)
{
    // some logic...

    factor.DoSomething();

    // some more logic...
}

...and you can come along at a later date and introduce a new form factor without having to change this code as you would with the hard-coded switch clause. You will also find this approach also lends itself easily to TDD (you should end up with this if you are doing TDD properly) as it is easy for mocking.

紫罗兰の梦幻 2024-09-10 14:54:46

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

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

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

I'd do it this way and forget the Maintainability index:

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

IMHO easy to read and easy to change.

凉栀 2024-09-10 14:54:46

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

I know this answer is very late, but I was interested that no one put up the dictionary solution yet. I've found that when dealing with huge switch statements that are data-oriented like this, it's often more readable to collapse the switch-case into a dictionary.

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

This gives you a Maintainability index of 74 - slightly lower than the array solution because of class coupling to a dictionary, but I feel it's more general because it works for any number of types you would normally switch on. Like the array solution, it scales very well and removes a lot of repetitive code.

Generally speaking, using a data-driven approach can help your code to be clearer, because it separates the important pieces (in this case, a condition and a result) from the cruft (in this case, the switch-case).

已下线请稍等 2024-09-10 14:54:46

我想到两件事:

使用枚举将描述和值结合起来

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
    }
}

Two things spring to mind:

Use an enum to marry up the description and value

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

Use a class or struct to represent each form factor

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
    }
}
蛮可爱 2024-09-10 14:54:46

我不知道这有多重要,但以下内容得到了 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];
}

I don't know how much it matters, but the following gets a 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];
}
深海里的那抹蓝 2024-09-10 14:54:46

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

Clearly for me the Enum method is the most maintainable as it doesn't involve hard-coded strings thus no typo problem and compile-time syntax check.
Only restrictions are naming rules.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文