代码之家  ›  专栏  ›  技术社区  ›  Chsir17

扔掉一个大开关盒

  •  0
  • Chsir17  · 技术社区  · 7 年前

    所以我一直在四处寻找,似乎摆脱大开关的正确答案是多态性,但我只是不知道如何将其从conditional改为poplymorphic。这是正确的解决方案吗?

    Console.WriteLine(@"Menu");
                Console.WriteLine(@"1.Create Account");
                Console.WriteLine(@"2.ATM");
                Console.WriteLine(@"3.Account info");
                Console.Write(@"Please enter your selection: ");
                var menuChoice = int.Parse(Console.ReadLine());
    
    
                switch (menuChoice)
                {
                    case 1:                       
                        atm.CreateAccount();
                        break;
                    case 2:
                        //Console.WriteLine(@"1.Deposit Or Withdraw");
                        Console.WriteLine(@"1.Deposit");
                        Console.WriteLine(@"2.Withdraw");
                        Console.Write(@"Please enter your selection: ");
                        var atmMenuChoice = int.Parse(Console.ReadLine());
                        switch (atmMenuChoice)
                        {
                            case 1:
                                atm.Deposit();
                                break;
                            case 2:
                                atm.Withdraw();
                                break;
                            default:
                                Console.WriteLine(@"Invalid selection!");
                                break;
                        }
                        break;
                    case 3:
                        atm.AccountInfo();
                        break;
                    default:
                        Console.WriteLine(@"Invalid selection!");
                        break;
                }
            }
    
    3 回复  |  直到 7 年前
        1
  •  2
  •   Scott Perham    7 年前

    在这种情况下,我倾向于使用 Dictionary<string, Action> 查找每个输入的操作。

    类似于:

    var actions = new Dictionary<string, Action>
    {
      { "1", atm.CreateAccount }
      { "2", AtmSelection } //This would do the same as below with the atmActions dictionary
      { "3", atm.AccountInfo }
    }
    
    var atmActions = new Dictionary<string, Action>
    {
      { "1", atm.Deposit }
      { "2", atm.Withdraw }
    }
    
    var input = GetInput(); //From stdin as you do currently
    
    if (actions.TryGetValue(input, out var action)) 
    {
        action();
    }
    else
    {
        Console.WriteLine("Invalid Selection");
    }
    

    我个人觉得这比大规模嵌套switch语句更容易阅读

        2
  •  0
  •   StackOverthrow    7 年前

    在使用某种序列化框架时,多态性优先于开关。想象一下你的 int 是单件类成员的序列化表示,所有单件类都有一个特定的操作方法(或 visits )您的 atm 对象然后可以反序列化实例并调用该方法:

    var foo = deserializer.deserialize(intVal);
    foo.doStuff(atm);
    

    仍然涉及到一个开关,但它位于序列化框架内,您不必维护它。如果要在没有序列化框架的情况下实现类似的模式,则必须自己编写开关。好处是您可以将交换机与其他逻辑分开:

    Foo GetFoo(int type) {
        // switch on type 
    }
    var foo = GetFoo(intVal);
    foo.doStuff(atm);
    

    这种模式是在没有(或没有)函数指针或等效对象的语言中开发的。在有函数指针的语言中 内景 另一个答案中建议的函数值基本上可以完成相同的事情。

        3
  •  0
  •   MikeH    7 年前

    我在这里可能有点疯狂,但这与Scott的答案类似。

    static IEnumerable<MenuItem> RootMenu;
    
    static void Main(string[] args)
    {
      RootMenu = BuildRootMenu();
      MenuItem.DisplayMenu(RootMenu, new Atm());
    }
    
    /// <summary>
    /// Creates the entire menu
    /// </summary>
    static IEnumerable<MenuItem> BuildRootMenu()
    {
      MenuItem item1 = new MenuItem() { DisplayText = "Create Account", AtmAction = (a) => a.CreateAccount() };
    
      MenuItem item2_1 = new MenuItem() { DisplayText = "Deposit", AtmAction = (a) => a.Deposit() };
      MenuItem item2_2 = new MenuItem() { DisplayText = "Withdraw", AtmAction = (a) => a.Withdraw() };
      MenuItem item2 = new MenuItem() { DisplayText = "ATM", AtmAction = (a) => MenuItem.DisplayMenu(new List<MenuItem> { item2_1, item2_2 }, a) };
    
      MenuItem item3 = new MenuItem() { DisplayText = "Account Info", AtmAction = (a) => a.CreateAccount() };
    
      return new List<MenuItem> { item1, item2, item3 };
    }
    
    class MenuItem
    {
      public String DisplayText;
      public Action<Atm> AtmAction = null;
    
      public void Execute(Atm atm)
      {
        AtmAction(atm);
        DisplayMenu(RootMenu, atm);
      }
    
      public static void DisplayMenu(IEnumerable<MenuItem> menuItems, Atm atm)
      {
        int i = 1;
        foreach (var mi in menuItems)
        {
          Console.WriteLine(i + ": " + mi.DisplayText);
          i++;
        }
    
        var rk = Console.ReadKey();
        menuItems.ToArray()[int.Parse(rk.KeyChar.ToString()) - 1].Execute(atm);
      }
    }
    
    
    class Atm
    {
      public void Deposit()
      {
        Console.WriteLine("Ran Deposit");
      }
      public void Withdraw()
      {
        Console.WriteLine("Ran Withdraw");
      }
      public void CreateAccount()
      {
        Console.WriteLine("Ran CreateAccount");
      }
      public void AccountInfo()
      {
        Console.WriteLine("Ran AccountInfo");
      }