假设:
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)请不要介意神奇的数字,在实际场景中我有有效的常数/来源。
答案 0 :(得分:2)
为了应用策略,它需要在具有选择策略的数据的对象中。您从经纪人,经纪人的交易阶段和按钮中提取数据。
与多态相结合需要三重调度,并且比当前代码要复杂得多。
您可能希望按阶段分开,并应用Demeter。
还有很多神奇的数字。如果要从一个系统转换到另一个系统,那么将它们转换为系统的常量,否则将它们移动到数据模型中。
答案 1 :(得分:2)
如果不了解更多关于代码的内容很难说,但问题似乎是你试图在方法中做多件事,同样,代码依赖于数据方法中的UI,看起来非常难看。
看起来你应该至少有两个方法,一个GetBrokerPrice和一个SetPriceOptions
答案 2 :(得分:2)
我想到了两个明显的重构:
else
:现在可以更轻松地重新排列ifs。if (phase == ...)
始终是外部的。如果需要,可以重新排序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()
完成
我认为它应该将ItemType
和ButtonEventArgs
的{{1}}传递给IBrkoer
的实例,并让它决定if-else逻辑。
就嵌套的if-else而言,我并不太关心。
业务逻辑可以像它想要的那样复杂,特别是当它缺乏统一性时
为这个逻辑制作一个查找表会更复杂,因为在这种情况下你需要一个三维表:哪个按钮,哪个代理交易阶段,然后是哪个参数要改变。说实话,这将是一件麻烦事。