代码之家  ›  专栏  ›  技术社区  ›  Manish Basantani

重构我的代码:基于不同变量的条件

  •  3
  • Manish Basantani  · 技术社区  · 14 年前

    鉴于:

    internal void Configure(ButtonEventArgs args, IBroker broker, FunctionEntry entry)
            {
                int phase = broker.TradingPhase;
    
                if (args.Button == ItemType.SendAutoButton)
                {                              
                    if (phase == 1)
                    {
                        entry.SetParameter("ANDealerPrice", -1);
                        entry.SetParameter("ANAutoUpdate", 4);
                    }
                    else if (phase == 2)
                    {
                        entry.SetParameter("ANDealerPrice", -1);
                        entry.SetParameter("ANAutoUpdate", 2);
                    }
                }
    
                if (phase == 1)
                {
                    if (broker.IsCashBMK)
                    {
                        entry.SetParameter("Value", 100);
    
                    }
                    else if (broker.IsCross)
                    {
    
                        entry.SetParameter("Value", 200);
    
                    }
                }
    }
    

    我正在寻找重构上述代码的建议。 正如Fowler所建议的:“用策略/多态性替换条件”,我未能在这些行上实现有效的代码。由于存在多个条件,基于多个变量。

    请建议是否有一个模式可以消除这些容易出错和丑陋的条件(代码气味)。

    谢谢你的关心。

    编辑: 1)我的目的是在这里应用开闭原则,这样如果明天逻辑发生变化,我可以通过引入一个新类来扩展这些条件。 2)请不要介意这些幻数,在真实场景中,我有它们的有效常量/源。

    5 回复  |  直到 14 年前
        1
  •  2
  •   Carl Manaster    14 年前

    对于您目前所介绍的内容,我倾向于将三个参数分离出来,每个参数都有自己的函数,因此:

    void SetAnDealerPrice(ButtonEventArgs args, IBroker broker,
            FunctionEntry entry) {
        if (args.Button != ItemType.SendAutoButton)
            return;
        int phase = broker.TradingPhase;
        if (phase == 1 || phase == 2)
            entry.SetParameter("ANDealerPrice", -1);
    }
    
    void SetAnAutoUpdate(ButtonEventArgs args, IBroker broker,
            FunctionEntry entry) {
        if (args.Button != ItemType.SendAutoButton)
            return;
        switch (broker.TradingPhase) {
        case 1:
            entry.SetParameter("ANAutoUpdate", 4);
            break;
        case 2:
            entry.SetParameter("ANAutoUpdate", 2);
            break;
        }
    }
    
    void SetValue(IBroker broker, FunctionEntry entry) {
        if (broker.TradingPhase != 1)
            return;
        entry.SetParameter("Value", broker.IsCashBMK ? 100 : 200);
    }
    

    这有点手工制作的(不适合在规则更改时半自动更新),而且效率略低(一些条件正在检查,一些字段被引用,多次,当然还有更多的函数调用)。我认为只有当你有了一个已经证明的问题(当你这样做的时候,需要比这些更大的改变),效率才是重要的,而且这种方法让你确切地知道当给定参数的规则改变时,要看什么样的代码。我不相信多态性会在这里引导您找到一个好的解决方案。

        2
  •  2
  •   Pete Kirkham    14 年前

    为了应用一个策略,它需要在对象中有选择策略的数据。你从经纪人、经纪人的交易阶段和一个按钮中提取数据。

    要与多态性结合使用,需要三次调度,而且比当前的代码复杂得多。

    您可能希望按阶段分离,并应用Demeter。

    还有很多神奇的数字。如果您正在从一个系统转换到另一个系统,那么将它们转换为要转换的系统的常量,否则将它们移动到数据模型中。

        3
  •  2
  •   Homde    14 年前

    在不了解更多代码的情况下很难说,但问题似乎是您试图在方法中做不止一件事,而且,在这样一个数据方法中,代码依赖于UI看起来很难看。

    似乎您至少应该有两种方法,一种是getbrokerprice,另一种是setpriceoptions

        4
  •  2
  •   Sjoerd    14 年前

    我们想到了两个明显的重构:

    1. 删除 else :现在可以更容易地重新排列IFS。
    2. 重新订购国际单项体育联合会,以便 if (phase == ...) 总是在外面。

    如果需要,可以重新排序if块以组合 if (phase == 1) 尽管我想重复一遍 如果(相位==1) 多次准备下一步。

    这些重构使得应用下面的重构更加容易。

    internal void Configure(ButtonEventArgs args, IBroker broker, FunctionEntry entry) 
    { 
            int phase = broker.TradingPhase; 
    
            if (phase == 1) 
            {                               
                if (args.Button == ItemType.SendAutoButton)
                { 
                    entry.SetParameter("ANDealerPrice", -1); 
                    entry.SetParameter("ANAutoUpdate", 4); 
                }
            }
            if (phase == 2) 
            {                               
                if (args.Button == ItemType.SendAutoButton) 
                { 
                    entry.SetParameter("ANDealerPrice", -1); 
                    entry.SetParameter("ANAutoUpdate", 2); 
                } 
            } 
            if (phase == 1) 
            { 
                if (broker.IsCashBMK) 
                { 
                    entry.SetParameter("Value", 100); 
    
                }
            }
            if (phase == 1)
            { 
                if (broker.IsCross) 
                { 
    
                    entry.SetParameter("Value", 200); 
    
                } 
            } 
    

    }

    现在您有一个小if块的长列表。这可以重构为 List<MyAction> . 在某些地方,您必须填充此列表,但遍历它非常简单:

    internal void Configure(ButtonEventArgs args, IBroker broker, FunctionEntry entry)  
    {
          foreach(var action in MyActions)
          {
               action(args, broker, entry);
          }  
     }
    internal void PopulateMyActions()
    {
          // Hopefully I have not made a syntax error in this code...
          MyActions.Add( (ButtonEventArgs args, IBroker broker, FunctionEntry entry) =>
             {
                if (broker.TradingPhase == 1) 
                {                               
                    if (args.Button == ItemType.SendAutoButton)
                    { 
                        entry.SetParameter("ANDealerPrice", -1); 
                        entry.SetParameter("ANAutoUpdate", 4); 
                    }
                }
            } );
          // And so on
    }
    

    另一种方法是为phase==1和phase==2创建单独的列表,并从 action :

    internal void Configure(ButtonEventArgs args, IBroker broker, FunctionEntry entry)  
    {
          int phase = broker.TradingPhase;
          foreach(var action in MyActions[phase])
          {
               action(args, entry);
          }  
     }
    
    internal void PopulateMyActions()
    {
          // Hopefully I have not made a syntax error in this code...
          MyActions[1].Add( (ButtonEventArgs args, FunctionEntry entry) =>
             {
                if (args.Button == ItemType.SendAutoButton)
                { 
                    entry.SetParameter("ANDealerPrice", -1); 
                    entry.SetParameter("ANAutoUpdate", 4); 
                }
            } );
          // And so on
    }
    

    我想我更喜欢后者,因为它使 phase 更明确。

    其他重构可以替换 action(args, entry) 具有 action(args.Button, entry) 但我不能判断这是否合适。

    将来,可以动态地填充列表,例如加载程序集时。然后可以通过配置设置控制要加载的程序集。Presto:切换行为而不重新编译核心代码!

    PS:代码没有经过测试,因为我现在离编译器很远。请随意编辑我的答案以删除语法错误,添加声明 MyActions 等等。

        5
  •  1
  •   BeemerGuy    14 年前

    在我看来,函数所做的只是设置 FunctionEntry .
    我会说 功能中心 应该处理这个逻辑。
    这不应该由 Configure() .
    我想应该通过 ItemType ButtonEventArgs 以及 IBrkoer 以…为例 功能中心 让它决定这是否是逻辑。

    至于嵌套的if else,我不太担心。
    业务逻辑可以像它想要的那样复杂,特别是当它缺乏一致性时。
    为这个逻辑创建一个查找表会更复杂,因为在这种情况下需要一个三维表:哪个按钮,哪个代理交易阶段,然后更改哪个参数。老实说,这将是一个棘手的问题。