在 C# 中清理超大箭头反模式

cleaning up super gross arrow head anti-pattern in c#

我对编码还很陌生,我希望尽早改掉坏习惯并开始编写干净高效的代码。我正在开发一个引用 API 的控制台应用程序,并且我有一系列深度嵌套的“if”(最高可达 10 层!)。

commonLogic forQuote = new commonLogic();

if (countryRes == CountryDes)
{
    //staying in country
    try2:
    //display reasons for travel
    Console.WriteLine("What Best Describes Your Reason For Traveling?");
    Console.WriteLine(" ");
    Console.WriteLine("1. United States Resident traveling Inside the U.S.");
    Console.WriteLine("2. Visiting United States For Business or Pleasure.");
    Console.WriteLine("3. Immigrating to The Unites States.");
    Console.WriteLine("4. Student, Faculty Member or Scholar With a J-1, F-1, H-3, M-1, or Q-1 Visa.");
    Console.WriteLine(" ");
    var x = Console.ReadLine();
    Console.Clear();

    if (x == "1")
    {
        //US resident
        //first print
        forQuote.gatherUserData();
    }
    else if (x == "2")
    {
        try3:
        //visiting the US
        Console.WriteLine("What Type of Coverage Do You Need?");
        Console.WriteLine(" "); 
        Console.WriteLine("1. Medical voerage");
        Console.WriteLine("2. Trip Cancellation");
        var r = Console.ReadLine();
        Console.WriteLine(" ");
        Console.Clear();

        if (r == "1")
        {
            //medical coverage
            Console.WriteLine("What Type of Medical Coverage Do You Want?");
            Console.WriteLine(" ");
            Console.WriteLine("1. Scheduled benifits");
            Console.WriteLine("2. Comprehensive Benifits");
            var s = Console.ReadLine();
            Console.WriteLine(" ");
            Console.Clear();

            if (s == "1")
            {
                //second print
                forQuote.gatherUserData();
            }
            else if (s == "2")
            {
                //comprehensive benifits
                //third print
                forQuote.gatherUserData();
            }
            else
            {
                //first else
                Console.WriteLine("Invalid Input. Please Try Again");
            }
        }
        else if (r == "2")
        {
            //trip canccelation
            //fourth print
            forQuote.gatherUserData();
        }
        else
        {
            //secondelse
            Console.WriteLine("Invalid Input. Please Try Again");
            goto try3;
        }
    }
    else if (x == "3")
    {
        //immigration
        //fithprint
        forQuote.gatherUserData();
    }
    else if (x == "4")
    {
        //students...
        //sixthprint
        forQuote.gatherUserData();
    }
    else
    {
        //thirdelse
        Console.WriteLine("Invalid Input. Please try Again");
        goto try2;
    }

}

这只是 if 总巢穴中的一小部分样本。我做了很多关于清理这个问题的研究,但我很难understanding/using找到我找到的答案。 我在重构时遇到的最大问题是 if 之后的每个都直接依赖于它之前的 if

我还对您需要达到每个 if 的输入进行了逻辑分析 table。如果有帮助,我会把它放在这里: Excel table showing if paths

非常感谢您的帮助,并且解释为什么您的回答提高了可读性和效率也将是极好的。

看这段代码:

bool loop = true;

while(loop)
{
    Console.WriteLine("Question");
    Console.WriteLine("1. Ans1");
    Console.WriteLine("2. Ans2");
    Console.WriteLine("3. Exit");
    string resp = Console.ReadLine();

    switch(resp)
    {
        case "1":
        Console.WriteLine("Ans1 chosen");
        break;
        case "2":
        SomeQuestion();
        break;
        case "3":
        loop = false;
        break;
        default:
        Console.WriteLine("Invalid Input. Please try Again");
        break;        
    }
}

void SomeQuestion()
{
    bool loop = true;

    while(loop)
    {
        Console.WriteLine("Question secon level");
        Console.WriteLine("1. Ans3");
        Console.WriteLine("2. Ans4");
        Console.WriteLine("3. Exit");
        string resp = Console.ReadLine();

        switch(resp)
        {
            case "1":
            Console.WriteLine("Ans1 chosen");
            break;
            case "2":
            break;
            case "3":
            loop = false;
            break;
            default:
            Console.WriteLine("Invalid Input. Please try Again");
            break;        
        }
    }    
}

这道题很简单,一道题就说明了思路。

  • 而是转到有条件的循环。
  • 相反,如果一个开关使代码更具可读性。

我们先分解一些眼前的问题。

goto 语句(错误):

此语句令人困惑,因为它不符合执行流程。它基本上说 "Go to this arbitrary point in code that you have to find"。现在将其乘以 3 或 4,您将得到一个带有许多传送点的迷宫。你会很快迷路。

以下是如何解决此问题的示例:

if(condition)
{
    try2:

    // do stuff

    if(condition2)
    {
        // Do more stuff
    }
    else
    {
        // Report error
        goto try2;
    }

}

// Replace with:

while(condition)
{
    // do stuff

    if(condition2)
    {
        // Do more stuff
        break;
    }
    else
    {
        // Report error
        continue; // Goes to the top of the loop
    }
}

这个例子并不完美,从技术上讲你不需要 "continue",但我想解释一下它的用法。

命名约定:

每个class名称、变量名称、方法名称等都需要一个描述性标识符。 "loop" 什么也没告诉我。命名尽可能具有描述性。如果这样做之后仍然存在一些歧义,在定义中使用注释来清除它

嵌套循环:

好的,这就是你程序的整体设计。不幸的是,询问一堆相关问题的控制台应用程序将会变得混乱。

为什么?

因为 WPF 等用户界面框架可以更好地处理此类逻辑。例如,您的很多逻辑都可以在按钮处理程序和控件属性中处理。

但是,您仍然可以改进您的代码。但是,它需要非常面向对象的设计。我会选择 状态模式 作为开始。

状态模式资源:http://www.dotnettricks.com/learn/designpatterns/state-design-pattern-c-sharp

然而,这是高级,如果您对classes、方法、对象、属性没有太多经验,则不容易理解,处理程序等。而且,它甚至不会减少您的工作量。事实上,它的增加是为了可读性和可测试性,如果这是概念验证或实践,您甚至可能不需要这两者。

因此,我的建议是一步一个脚印地学习知识。开发最强大的解决方案是件好事,但如果您刚刚开始,就不是这样了。一次拿起一件,直到你有足够的经验将它们放在一起。