【问题标题】:cleaning up super gross arrow head anti-pattern in c#在 C# 中清理超粗箭头反模式
【发布时间】:2017-12-18 10:47:42
【问题描述】:

我对编码还很陌生,我希望尽早改掉坏习惯,开始编写干净高效的代码。我正在开发一个引用 API 的控制台应用程序,并且我有一系列深度嵌套的 'if's(点深度可达 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;
    }

}

这只是ifs 这个总巢的一小部分。我已经做了很多关于清理这个问题的研究,并且很难理解/使用我找到的答案。 我在重构中遇到的最大问题是,if 之后的每个if 都直接依赖于它之前的if

我还为每个if 所需的输入制作了一个逻辑表。如果有帮助,我会把它放在这里: Excel table showing if paths

我非常感谢您的帮助,并且解释为什么您的答案会提高可读性和效率也会非常好。

【问题讨论】:

  • 我知道你才刚刚开始,所以我不会因为使用 goto 而对你大吼大叫,但是不要使用 goto
  • 我可以用一句话给你的最大提示是永远不要使用“goto”。该语句总是可以用某种循环替换。了解循环的“break”和“continue”关键字。此外,始终有意义地命名类和方法。 “loop.loops1”是无用且令人困惑的。
  • @maccettura 我有 goto,因此如果使用了无效输入,它会将您带回到循环的开头。我知道这是不好的做法,但我不知道更好的方法。
  • 您还可以将其中一些操作分解为处理每个“菜单”的无效方法。这样,您的代码就不会一团糟,而是模块化且整洁的。
  • @RaptorJesus 如果您知道这是不好的做法,那么您应该花更多时间在教程上,然后退后一步看看其他方法来解决这个问题。永远不要做不好的事情,因为你想不出更好的方法。找到更好的方法!走下坡路就是我之前说的。将您的变量和方法命名为更具描述性的名称,将您的逻辑分解为多个部分并重新组织。让每个方法负责一件事(比如导航一个菜单)。

标签: c# if-statement nested-loops


【解决方案1】:

看看这段代码:

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 循环。
  • 如果一个开关使代码更具可读性。

【讨论】:

  • 这不会导致一堆嵌套开关吗?这比嵌套 ifs 更糟还是更好?还是我会让 case"2" 突破这个 while 进入另一个 while 来处理后续 ifs 块的逻辑?
  • @RaptorJesus 不幸的是,对于控制台应用程序,您会遇到很多嵌套问题。控制台应用程序对于大量用户交互来说是糟糕的 UI。发布重要的事情是完全摆脱您的goto。如果你把事情分解成处理每个菜单的单独方法,那么这不会是一团糟,它会很干净
  • 我认为开关比 ifs 更具可读性 :-) 我用第二级改进了我的例子
  • 这样代码的可读性更高。正如@maccettura 从不使用 goto 所说的那样。
  • @RaptorJesus 示例改进,这样你总是有一个级别的切换
【解决方案2】:

让我们先分解一些直接的问题。

goto 语句(错误):

此语句令人困惑,因为它不符合执行流程。它基本上说“转到您必须找到的代码中的任意点”。现在将它乘以 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
    }
}

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

命名约定:

每个类名、变量名、方法名等都需要一个描述性标识符。 “循环”什么也没告诉我。命名时尽可能具有描述性。如果这样做后仍有一些歧义,请在定义中使用 cmets 清除它

嵌套循环:

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

为什么?

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

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

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

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

因此,我的建议是逐步了解您的知识。开发最强大的解决方案是好的,但如果您刚刚开始,则不是。一次一个地拾起每一块,直到您有足够的经验将它们全部组合在一起。

【讨论】:

  • 这是一个非常好的答案!谢谢你生病调查所有这些东西。我同意控制台应用程序不是这个项目的最佳选择,毕竟在重新创建 Web 应用程序时。所以我试图编写通常以按钮或下拉菜单形式处理的东西。但我没有将其作为网络应用程序的技能或经验。
  • @RaptorJesus 制作网络应用程序可以说比尝试在控制台应用程序中编写它更容易。查看 ASP.NET MVC & Bootstrap。大量样板和脚手架可以为您处理大量工作。
  • 不仅如此,控制台应用程序代码也不会移植到 Web 应用程序中。无论如何,您将重写其中的绝大部分。如果您只是在您想要的平台上尝试它可能会更好。如果你不喜欢成品,去学习,回来,再做一次。我正在开发我的库的第四次迭代,它甚至还没有发布:)
猜你喜欢
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
  • 2016-08-01
  • 1970-01-01
  • 2020-10-04
  • 1970-01-01
  • 1970-01-01
  • 2019-05-07
相关资源
最近更新 更多