【问题标题】:What is the best way to fix "IFs hell"?解决“IFs地狱”的最佳方法是什么?
【发布时间】:2019-10-05 22:24:54
【问题描述】:

我有一个 Deal 类,它有几个状态:New、Bought、Finished、趋势和交叉属性。交易实例可以随时改变趋势和交叉 - 取决于市场状况。

取决于当前的“趋势”、“交叉”和其他条件,交易可以以不同的方式处理

我尝试了战略和责任链模式,但我的知识和经验不足以正确地做到这一点。而且我不确定是否做出了正确的决定。

现在它可以工作了,但它是一个地狱

async def check_deal(deal):

    if deal.is_new():
        if deal.current_trend == UP_TREND and CROSSED_TOP_GREEN in deal.crossing:
            return await deal.create_order(CROSSED_TOP_GREEN)

        if deal.current_trend == FLAT_TREND and CROSSED_BOTTOM_ORANGE in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM_ORANGE)

        if deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM)

    if deal.is_bought() and not deal.candle_has_order:
        if deal.current_trend == UP_TREND and CROSSED_BOTTOM_GREEN in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM_GREEN)
        else:
            return await deal.update_deal()

        if deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing:
            return await deal.create_order(CROSSED_BOTTOM)
        else:
            return await deal.update_deal()

    else:
        return await deal.update_deal()

    if deal.is_profitable() and len(deal.orders) == 1 :
        if deal.current_trend == UP_TREND  and CROSSED_TOP in deal.crossing:
            return await deal.trailing_enable(CROSSED_TOP)

        if deal.current_trend == FLAT_TREND and CROSSED_TOP_GREEN in deal.crossing:
            return await deal.trailing_enable(CROSSED_TOP_GREEN)

        if deal.current_trend == DOWN_TREND and CROSSED_BASIS in deal.crossing:
            return await deal.trailing_enable(CROSSED_BASIS)

    if deal.is_profitable() and len(deal.orders) > 1:
        if deal.current_trend == UP_TREND and CROSSED_BASIS in deal.crossing:
            return await deal.trailing_enable(CROSSED_BASIS)

        if deal.current_trend == DOWN_TREND and  CROSSED_BOTTOM_GREEN in deal.crossing:
            return await deal.trailing_enable(CROSSED_BOTTOM_GREEN)

规则

当前的交叉点是一个变量,可以随时不同 当前趋势是一个变量,随时可能不同

我们在其中一种情况下开始交易:

  1. Deal.is_new()(交易未保存到数据库)且当前趋势为 UP,当前交叉点位于 deal.crossing 中
  2. Deal.is_new() 并且当前趋势是 FLAT 并且当前交叉点在 deal.crossing 中
  3. Deal.is_new() 且当前趋势为 DOWN,当前交叉点在 deal.crossing 中

我们在其中一种情况下为当前交易增加资金:

  1. Deal.is_bought()(交易在数据库中)并且当前趋势为 UP,当前交叉点在 deal.crossing 中
  2. Deal.is_bought()(交易在数据库中)并且当前趋势是DOWN并且当前交叉在deal.crossing中

我们在其中一种情况下从交易中获利:

  1. 交易有利可图,交易只有 1 个订单,当前趋势为 UP,当前交叉点在 deal.crossing 中
  2. 交易有利可图,交易只有 1 个订单,当前趋势为 FLAT,当前交叉点在 deal.crossing 中
  3. 交易有利可图,交易只有 1 个订单,当前趋势为 DOWN,当前交叉点在 deal.crossing 中
  4. 交易有利可图且交易有超过 1 个订单且当前趋势为 UP 且当前交叉处于 deal.crossing 中
  5. 交易有利可图且交易有超过 1 个订单且当前趋势为持平且当前交叉处于 deal.crossing 中
  6. 交易有利可图且交易有超过 1 个订单且当前趋势为 DOWN 且当前交叉点在 deal.crossing 中

如果没有达到 deal.is_bought() 和任何其他条件,我们只会更新交易

【问题讨论】:

    标签: python python-3.x oop design-patterns


    【解决方案1】:

    这里的情况是:当满足条件时,需要执行动作

    您可以定义一个接口(您可以使用简单的基类或抽象基类)来捕获它。然后为各种if's -> action添加实现这个接口(从基类继承)的类。

    这是一个例子:

    class Rule:
    
        def isMetBy(self, deal):
            pass
    
        def execute(self, deal):
            pass
    
    class Rule1(Rule):
    
        def isMetBy(self, deal):
            return deal.current_trend == FLAT_TREND and CROSSED_BOTTOM_ORANGE in deal.crossing
    
        async def execute(self, deal):
            return await deal.create_order(CROSSED_BOTTOM_ORANGE)
    
    class Rule2(Rule):
    
        def isMetBy(self, deal):
            return deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing
    
        async def execute(self, deal):
            return await deal.create_order(CROSSED_BOTTOM)
    
    rules = [Rule1(), Rule2()]
    
    async def check_deal(deal):
    
        for rule in rules:
            if(rule.isMetBy(deal)):
                return await rule.execute(deal)
    

    每个规则都捕获一个 if 语句,即您的代码。这样,您可以根据需要定义任意数量的规则。添加新的只是添加另一个类的问题。如果可以的话,你会用好名字命名你的规则,所以这将增加好处或命名你的规则。我使用 Rule1 和 Rule2 因为我不知道你的规则是什么意思。例如,您可以有类似(我只是为了说明目的而编造的)EnableTrailingForProfitableDealsRule

    在您的情况下,您有一个 if,其中包含多个 subif。您可以将它们解耦到不同的类,或者您可以使用Composite pattern 创建带有子规则规则

    这是一个例子:

    class CompositeRule(Rule):
        subRules = []
    
        def __init(self):
            self.subRules = [Rule1(), Rule2()]
    
        def isMetBy(self, deal):
    
            if deal.is_profitable() and len(deal.orders) == 1 :
                for rule in self.subRules:
                    if(rule.isMetBy(deal)):
                        return true;
    
            return false
    
        async def execute(self, deal);
    
            for rule in self.subRules:
                if(rule.isMetBy(deal)):
                    return await rule.execute(deal)
    

    CompositeRules 可以使代码更容易或更难理解。如果您的代码使用CompositeRules 更难理解,您可以改用简单的规则。许多这些简单的规则在其中都有相同的 if 语句,但这通常不是问题。

    这里将CompositeRule 分解为两个简单的规则:

    class Rule1(Rule):
    
        def isMetBy(self, deal):
            return if deal.is_profitable() and len(deal.orders) == 1 and deal.current_trend == UP_TREND  and CROSSED_TOP in deal.crossing
    
        async def execute(self, deal):
            return await deal.trailing_enable(CROSSED_TOP)
    
    class Rule2(Rule):
    
        def isMetBy(self, deal):
            return if deal.is_profitable() and len(deal.orders) == 1 and  deal.current_trend == FLAT_TREND and CROSSED_TOP_GREEN in deal.crossing:
    
        async def execute(self, deal):
            return await deal.trailing_enable(CROSSED_TOP_GREEN)
    

    您可以尝试并选择最适合您的方法。

    【讨论】:

    • 这就是我要找的!非常感谢!
    【解决方案2】:

    代码复杂的一个原因是您正在以 过程 方式处理 对象check_deal 正在检查交易的属性并根据它们做出决策,然后调用交易的方法。这个逻辑属于Deal 类,比如Deal.check 方法。

    将逻辑移入Deal 类并没有降低其复杂性,但它为不同的设计选择开辟了道路。

    例如,与其在条件代码块中检查Deal 是新的还是购买的还是完成的,您可以拥有NewDealBoughtDealFinishedDeal 子类,它们的check 方法只与影响这些特定类型的决策。

    如果类型的子类化在您的应用程序中没有意义,则策略模式可能是解决此问题的另一种方法 - 一个 Deal 类,但根据交易类型提供不同的检查策略。

    另一种方法可能是将Deal 的生命周期中的事件建模为状态机。创建代表生命周期中每个阶段的State 类,它们知道要采取的适当行动,以及如何决定下一个状态应该是什么(责任链在这里可能很有用)。这可能需要仔细建模以防止DealState 之间的过度耦合。

    简而言之,尝试构建您的业务逻辑,以便您拥有不必做出决定的类:它们只是做正确的事情,或者至少用最少的条件代码做正确的事情。

    【讨论】:

      【解决方案3】:

      如果您使用“elif”,您的计算机工作会更轻松一些,因为它会首先检查“if”,如果它是真的,它会忽略所有连接到“if”的“elif”和“else”,但是如果您在每种情况下都使用“if”(就像您一样),您的计算机将通过每个“if”语句并检查它。我希望这是有道理的。

      【讨论】:

        【解决方案4】:

        现在它可以工作了,但它是一个地狱。

        您的if-logic 有缺陷 - 您的代码无法访问:

        if deal.is_bought() and not deal.candle_has_order:
            if deal.current_trend == UP_TREND and CROSSED_BOTTOM_GREEN in deal.crossing:
                return await deal.create_order(CROSSED_BOTTOM_GREEN)
            else:
                return await deal.update_deal()
        
            # Neither of the following will EVER happen, you already 
            # left the function with the _else_ from above:
            if deal.current_trend == DOWN_TREND and CROSSED_BOTTOM in deal.crossing:
                return await deal.create_order(CROSSED_BOTTOM)
            else:
                return await deal.update_deal()
        

        我尝试了战略和责任链模式,但我的知识 经验不足以正确地做到这一点。我不确定 做出了正确的决定。

        在不了解您的规则 的情况下,我们无法提供帮助。如果你把所有规则都写下来,它可能会变得过于宽泛在这里无法解决。

        【讨论】:

        • 谢谢,如果错过了。我在帖子中添加了规则
        猜你喜欢
        • 2010-11-08
        • 1970-01-01
        • 2011-01-30
        • 2016-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多