我正在开发一个不久将用于配置的应用程序。情况并不复杂。该应用程序管理有权访问服务的组。根据组访问的服务,应用不同的业务规则。
第一个想法(简单和愚蠢)是有一个大的if / else或switch / case来覆盖所有的情况。以下是伪代码的样子:
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没有与其他策略相关的业务规则(它只是自己有一个动作)。然后,也许我不明白这种模式。
你知道如何以最优雅的方式处理这种情况吗?我知道将来我会有新的服务,所以我现在不想犯错误。 我希望我不需要为每个组合编写策略(3个服务的7个策略,4个服务的14个策略,它会很快!)
先谢谢你的帮助:)
答案 0 :(得分: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收集某些策略接口的所有实现,然后选择适用于某些条件/上下文的那个。