如何降低这些 if else 语句的圈复杂度

How to reduce cyclomatic complexity for these if else statements

我正在尝试验证命令行参数并在出现错误时打印错误消息。

我的问题是,如果增加命令行参数的数量(目前,我只有 3 个),那么我的代码就会变成意大利面条代码。如何降低给定代码的圈复杂度?

var isCmdLineWrong = false;
var Arg1 = "Undefined";
var Arg2 = "Undefined";
var Arg3 = "Undefined";

var commandArguments = Environment.GetCommandLineArgs();
if (commandArguments.Contains("-r") && arguments[commandArguments.IndexOf("-r") + 1].StartsWith("-") == false)
    Arg1 = commandArguments[commandArguments.IndexOf("-r") + 1];
else
{
    isCmdLineWrong = true;
}
if (commandArguments.Contains("-n") && commandArguments[commandArguments.IndexOf("-n") + 1].StartsWith("-") == false)
    Arg2 = commandArguments[commandArguments.IndexOf("-n") + 1];
else
{
    isCmdLineWrong = true;
}
if (commandArguments.Contains("-p") && commandArguments[commandArguments.IndexOf("-p") + 1].StartsWith("-") == false)
    Arg3 = commandArguments[commandArguments.IndexOf("-p") + 1];
else
{
    isCmdLineWrong = true;
}
if (isCmdLineWrong) Console.WriteLine("Parameters structure is inconsistent");

我建议提取 CommandLine class:

public static class CommandLine {
  private static String FindValue(string value) {
    var commandArguments = Environment.GetCommandLineArgs();

    int index = commandArguments.IndexOf(value);

    if (index < 0)
      return null; 
    else if (index >= commandArguments.Length - 1)
      return null; // cmd like "myRoutine.exe -p" 
    else 
      return commandArguments[index + 1];  
  } 

  static CommandLine() {
    Arg1 = FindValue("-r");
    Arg2 = FindValue("-n");
    Arg3 = FindValue("-p");
  } 

  public static String Arg1 { get; private set; }

  public static String Arg2 { get; private set; }

  public static String Arg3 { get; private set; }

  public static bool IsValid {
    get {
      return Arg1 != null && Arg2 != null && Arg3 != null;
    }
  }
}

写完这个class你可以把

if (!CommandLine.IsValid) {
  Console.WriteLine("Parameters structure is inconsistent");

  return;
} 

if (CommandLine.Arg1 == "quit") {
  ...
}  

可能在您的代码中观察到的最重要的事情是 您多次执行完全相同的事情,尽管输入 "-r" 和 [=14] =]、"-n"Arg2"-p"Arg3。也就是说,您有以下代码片段出现了三次(减去我的重新格式化):

if (commandArguments.Contains(…) &&
    arguments[commandArguments.IndexOf(…) + 1].StartsWith("-") == false)
{
    … = commandArguments[commandArguments.IndexOf(…) + 1];
}
else
{
    isCmdLineWrong = true;
}

Don't Repeat Yourself (DRY) principle 试图警告我们不要编写复制粘贴式的重复代码,而您的原始代码明显违反了它。

我建议您提取公共代码并将其放在单独的方法中。例如:

static bool TryGetArg(string commandArguments, string name, out string value)
{
    // Debug.Assert(name.StartsWith("-"));
    if (commandArguments.Contains("-") &&
        arguments[commandArguments.IndexOf(name) + 1].StartsWith("-") == false)
    {
        value = commandArguments[commandArguments.IndexOf(name) + 1];
        return true;
    }
    else
    {
        value = null;
        return false;
    }
}

现在您将重复的 if else 替换为以下内容:

string commandArguments = Environment.GetCommandLineArgs();

string arg1 = null;
string arg2 = null;
string arg3 = null;
bool isCmdLineOk = TryGetArg(commandArguments, "-r", out arg1) &&
                   TryGetArg(commandArguments, "-n", out arg2) &&
                   TryGetArg(commandArguments, "-p", out arg3);
if (isCmdLineOk)
{
    // do something with `arg1`, `arg2`, `arg3`.
}
else
{
    // not all of `arg1`, `arg2`, `arg3` could be set to a value.
    Console.WriteLine("Parameters structure is inconsistent");
}

这个问题是如何重用代码的一个简单示例。

  • 查找似乎是 copied/pasted、
  • 的代码
  • 放在一个函数里,
  • 副本之间的任何差异,将它们作为参数传递,
  • 用函数调用替换副本。

结果是

    // Returns this option's value from args, or null on error
    public string OptionValue(string[] args, string option)
    {
        try
        {
            if (args.Contains(option))
            {
                string value = args[args.IndexOf(option) + 1];  // reuse expressions as well

                if (!value.StartsWith("-"))
                    return value;
            }

            return null;    // null meaning "undefined"
        }
        catch
        {
            return null;  
        }
     }

     // And now your code
     string[] args = Environment.GetCommandLineArgs();

     string Arg1 = OptionValue(args, "-r"); 
     string Arg2 = OptionValue(args, "-n"); 
     string Arg3 = OptionValue(args, "-p"); 

     bool isCmdLineWrong = (Arg1 == null ||
                            Arg2 == null ||
                            Arg3 == null);

当然,如果您没有 copy/paste 代码作为开始,那么所有这些重写都是可以避免的。