【问题标题】:Java - Complex Decision TableJava - 复杂的决策表
【发布时间】:2017-09-23 14:15:53
【问题描述】:

目前必须创建一个决策表,该决策表基于不同的对象,每个对象可以具有不同/多个信息。

例子:

object A can be: OK / KO
object B can be: OK / WARNING / KO
object C can be: STARTING / RUNNING / ENDING

根据 A、B 和 C 的状态,我们得到不同的输出。我目前做了很多 IF 语句,感觉这是非常菜鸟式的编码。

像这样:

if (C == STARTED) {
    if (A == OK) {
        if (B == OK)
            return "something";
        if (B == WARNING)
            return "something else";
        else
            return "something more";
    }
  ...
}

是否有任何已知的技术(使用 hasmap 或 Java 8 中已有的东西)以适当的方式处理此类问题?

想过实现一个决策树,但我当前的 if 语句大约需要 30 行,并且不确定创建(一堆)全新的类来解决这个问题是否明智。

【问题讨论】:

标签: java


【解决方案1】:

事实上,这里有三种不同的情况:

if (C == STARTED) {
    if (A == OK) {
        if (B == OK)
            return "something";
        if (B == WARNING)
            return "something else";
        else
            return "something more";
    }
  ...
}

在不同的方法中分离不同的案例或在一个类中的每个案例允许对其进行统一测试,轻松理解每个规则并对其进行更改,而不会对其他规则产生副作用。

例如:

if (C == STARTED && (A == OK) && (B == OK) 是一个规则

if (C == STARTED && (A == OK) && (B == WARNING) 是另一条规则

if (C == STARTED && (A == OK) && (B == KO) 是另一条规则

要么为每个规则创建一个方法,要么为一个处理方法创建一个接口,并使每个规则成为它的不同实现。
责任链是这种方式的一个很好的模式。

例如,您可以拥有这些。

规则界面:

public interface IRule {     
    public void setNextRule(IRule nextRule);
    public abstract boolean apply(Data data);     
}

规则接口的抽象公共类:

public abstract class AbstractRule implements IRule {

    protected IRule nextRule;

    public void setNextRule(IRule nextRule) {
       this.nextRule = nextRule;
    }

    public boolean applyNextRuleIfExist(Data data) {
      if (this.nextRule != null) {
         return this.nextRule.apply(data);
      }
      return false;
    }

}

具体规则:

public class RuleXXX extends AbstractRule {

    public boolean apply(Data data) {
        if (data.C == STARTED && (data.A == OK) && (data.B == OK){
           return true;
         }
       return applyNextRuleIfExist(inputDataForDiscountRules);     
    }          
}

最后你可以创建规则链并使用它:

IRule firstRule = new RuleXXX();
firstRule.setNextRule(new RuleYYY());
...
// apply the chain
Data data = ...;
firstRule.apply(data);

【讨论】:

    【解决方案2】:

    有很多方法。如果这些状态是枚举值、整数或字符串,则 switch 看起来比许多嵌套的 ifs 更干净,尽管归根结底是同一件事,但最终仍可能会出现一些深层嵌套。

    switch (C) {
        case STARTED : 
            {
                // do stuff for this state
            }
        case RUNNING :
            {
                // do stuff for this state
            }
    }
    

    在必要时不要忘记break; 声明,否则案件可能会失败。不过,在 return 语句之后就没有必要了。

    更进一步的方法是简单地分解方法。

    switch (C) {
        case STARTED : return handleStartedState(A, B);
        case RUNNING : return handleRunningState(A, B);
    }
    

    然后,您可以在一个单独的方法中为每个 C 案例实现逻辑,这反过来又对其他输入执行相同的操作。这样做的缺点是,您最终可能会为各种输入组合使用大量具有长名称的方法。

    也许更好的面向对象方法是将如何处理各种状态组合的责任放在类本身中。我不知道您的 A、B 和 C 是什么,但如果它们是您控制的类,您可以根据需要在每个委托中创建方法。例如,C 类有一个方法handle(A, B),它根据自己的状态调用A 上的方法,A 然后对B 执行相同的操作。解决此问题的一种方法是使用the state pattern,这是一种将行为与某些对象的状态相结合的设计模式。

    这确实意味着您正在创建一些类只是为了处理这个问题,并且它可能会使根本不需要需要那么复杂的事情过于复杂。问自己这些问题:

    • 以后会不会有更多的案件需要处理?
    • 添加更多案例的情况是否经常发生?
    • 是否必须能够以动态方式扩展功能(可能由客户使用代码)?
    • 添加新案例会导致代码长度呈指数增长,而不是线性增长吗?

    如果其中任何一个是正确的,您可以考虑采用更复杂的方法。但是,如果几十行 if 语句可以很好地完成工作,并且它们不太可能很快改变并且改变不是重构的噩梦,那么它们可能是完成这项工作的正确工具。虽然我重视前期设计并展望未来,但很容易被骗去进行过于通用的设计,最终成为火箭飞船,而自行车就足够了。换句话说,避免being an "architecture astronaut"。开发中的一条众所周知的规则是始终从可能可行的最简单的事情开始。我不认为这是绝对的。如果问题开始扩大,重构可能会变得困难,而某些前期设计可能从一开始就很理想。但通常这是一个很好的初始方法。

    还意识到模式可以提供答案,但它们本身的存在得益于其环境的优雅:面向对象编程。 Java 是kingdom of nouns, of "things", when sometimes what you need are verbs, "actions"。函数式编程允许一些需要面向对象模式的事情自然而然地完成,以至于您甚至没有意识到它有一个模式。 Java 8 朝着函数式编程迈出了重要的一步,因此您可能想研究如何链接行为而不是类。

    最后,对于倾向于扩展或更改的复杂规则集,您需要查看规则引擎。正如 freedev 在评论中指出的那样,Drools is a Java solution for this。

    这个问题没有“一刀切”的答案,因为它过多地依赖于项目、上下文、代码库的其余部分......所以希望上述信息能让您找到最有效的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-08
      • 2020-01-22
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      • 1970-01-01
      • 2014-11-25
      • 2011-06-23
      相关资源
      最近更新 更多