2016-11-07 39 views
1

我正在研究即将用于供应的应用程序。情况并不复杂。该应用程序管理有权访问服务的组。根据组可以访问的服务,应用不同的业务规则。在没有'switch'/'if'语句的情况下处理具有“复杂”条件的策略

第一个想法(简单和愚蠢)将是一个大的if/else或开关/案例,将涵盖所有的情况。这里是如何可能看起来像伪代码:

if (serviceA OR serviceB) { 
    doActionA(); 
} 
if(serviceA AND serviceC) { 
    doActionA(); 
    doActionB(); 
} 
if(serviceB AND serviceC) { 
    doActionA(); 
    doActionC(); 
} 
if(serviceA AND serviceB AND serviceC) { 
    doActionA(); 
    doActionB(); 
    doActionC(); 
} 
if(serviceD) { 
    doActionD(); 
} 

这是不是很方便,因为如果添加新的服务,我需要更新此声明(这可能是数百/千线的长!)。

在意识到这一点之后,我做了一些关于哪种设计模式可以帮助我解决这个问题的研究。看起来像这样的人可能会帮助我成为战略模式。再次,它不会改变任何东西(或者我不明白),代码是这样的:

if (serviceA OR serviceB) { 
    setStrategy(new StrategyA); 
} 
if(serviceA AND serviceC) { 
    setStrategy(new StrategyB); 
} 
if(serviceB AND serviceC) { 
    setStrategy(new StrategyC); 
} 
if(serviceA AND serviceB AND serviceC) { 
    setStrategy(new StrategyD); 
} 
if(serviceD) { 
    setStrategy(new StrategyE); 
} 

strategy.run(); 

在某种程度上,这是更糟糕,因为我只能有一个策略,并通知serviceD如何没有与他人相关的商业规则(它只是有一个自己的行动)。然后,也许我不明白这种模式。

你有什么想法我怎么能以最优雅的方式处理这种情况?我知道将来我会有新的服务,所以我现在不想犯错。 我希望我不需要写策略每个组合(7个策略3个服务,14个策略4个服务,它跑得快!)

感谢您通过您的帮助:)

回答

1

不用使事情变得不必要的复杂与商业规则和所有动物园,我只是定义谓词策略对。

对于谓词你需要某种形式的Context这将允许检查context.isServiceA()

对于你可能还需要某种ExecutionContext策略(也许不是,如果你的策略是自载)。

因此,一个Predicate<Context>将是一个检查,如果一个策略是适用的,并且Consumer<ExecutionContext>将是战略。那么你只需要收集一些Predicate<Context>/Consumer<ExecutionContext>对。如果您不想编写任何其他类,请使用LinkedHashMap。例如:

Map<Predicate<Context>, Consumer<ExecutionContext> rules = new LinkedHashMap<>(); 

rules.put(ctx -> ctx.isServiceA() || ctx.isServiceB(), new StrategyA()); 
rules.put(ctx -> ctx.isServiceA() && ctx.isServiceC(), new StrategyB()); 

依此类推。

句法糖,你可以定义之类的东西ctx -> ctx.isServiceA()为常数,然后

rules.put(SERVICE_A.or(SERVICE_B), new StrategyA()); 

在评估过程中你只是遍历条目和执行的第一个合适的策略。例如:

rules 
.entrySet() 
.filter(entry -> entry.getKey().test(context)) 
.findFirst() 
.map(Entry::getValue) 
.ifPresent(strategy -> strategy.accept(executionContext)); 

另一个想法是让策略本身决定它们是否适用。所以一个策略将有一个test方法(或谓词)和apply方法。然后,您只需寻找适用的策略即可执行。我经常与Spring bean自动发现结合使用。一些中心bean收集一些策略接口的所有实现,然后选择适用于某些条件/上下文的那个。

+0

感谢您的回答!不幸的是,我不是用java而是用php工作。不过,我想我明白你的意思。基本上你建议在某种配置文件中编写规则,在我的“主”中循环我的规则,然后执行那些正常的规则,对吧?有了这个解决方案,无论我需要一个适用于每种情况的规则(针对每个组合的规则),这都不方便,或者我可能会多次调用相同的策略(在我的示例中,如果我有serviceA和serviceB,它会执行actionA两次)。 – Ryuu

+0

我建议,一个策略在适用时也会提供一个谓词。你如何编码这完全取决于你。不,您不一定需要针对每种情况或重复调用的规则。就像在你发布的代码中,你有一些命令,一些规则的优先级。一旦你找到了一个最合适的规则,你只需调用策略并返回。你不需要进一步迭代。 – lexicore

+0

我明白了。但是,如果我不重复,我仍然必须使用多种策略。如果小组有服务A和D,我有两种不同的行动(所以2种策略)。令我困扰的是,我将拥有完全相同的操作但具有不同参数的策略,并且我可能无法为该策略和其他策略创建足够通用的接口。我的另一个(也是最后一个)问题是我有多个涉及同一外部应用程序的操作。如果我在X战略中削减他们,我将不得不调用X时间相同的WS来获取战略信息。你知道我可以预防吗? – Ryuu

相关问题