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

时间:2010-11-20 10:48:47

标签: c# design-patterns refactoring

假设:

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);

                }
            }
}

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

请建议是否有可以消除这些容易出错且难看的条件(代码气味)的模式。

感谢您的关注。

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

5 个答案:

答案 0 :(得分:2)

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

与多态相结合需要三重调度,并且比当前代码要复杂得多。

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

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

答案 1 :(得分:2)

如果不了解更多关于代码的内容很难说,但问题似乎是你试图在方法中做多件事,同样,代码依赖于数据方法中的UI,看起来非常难看。

看起来你应该至少有两个方法,一个GetBrokerPrice和一个SetPriceOptions

答案 2 :(得分:2)

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

  1. 删除else:现在可以更轻松地重新排列ifs。
  2. 重新排序if,以便if (phase == ...)始终是外部的。
  3. 如果需要,可以重新排序if-blocks以组合if (phase == 1)块,但我想我会多次重复if (phase == 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
    }
    

    另一种方法是为阶段== 1和阶段== 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的声明,等等。

答案 3 :(得分:2)

对于你到目前为止所呈现的内容,我倾向于将这三个参数分开,每个参数都有自己的功能,因此:

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);
}

这有点手工制作(随着规则的变化,不适合半自动更新),效率稍差(有些条件正在检查,有些字段被引用,多次,当然还有更多功能调用)。在你遇到问题之前我并不认为效率很重要(当你这样做时,需要比这些更大的更改),这种方法让你知道在给定参数的规则发生变化时要查看的代码。我不相信多态性可能会让你在这里找到一个好的解决方案。

答案 4 :(得分:1)

在我看来,所有功能正在做的是设置FunctionEntry的字符串参数 我会说FunctionEntry应该处理这个逻辑 这不应该由Configure()完成 我认为它应该将ItemTypeButtonEventArgs的{​​{1}}传递给IBrkoer的实例,并让它决定if-else逻辑。

就嵌套的if-else而言,我并不太关心。
业务逻辑可以像它想要的那样复杂,特别是当它缺乏统一性时 为这个逻辑制作一个查找表会更复杂,因为在这种情况下你需要一个三维表:哪个按钮,哪个代理交易阶段,然后是哪个参数要改变。说实话,这将是一件麻烦事。