根据业务逻辑,其中一种方法的输出用作另一种方法的输入。逻辑具有线性流动。 为了模拟行为,现在有一个控制器类可以包含所有内容。
它非常混乱,太多loc并且难以修改。此外,异常处理非常复杂。单个方法会进行一些处理,但全局异常会冒泡,并且涉及大量try
catch
语句。
是否存在解决此问题的设计模式?
示例控制器类代码
try{
Logic1Inputs logic1_inputs = new Logic1Inputs( ...<some other params>... );
Logic1 l = new Logic1(logic1_inputs);
try{
Logic1Output l1Output = l.execute();
} catch( Logic1Exception l1Exception) {
// exception handling
}
Logic2Inputs logic2_inputs = new Logic2Inputs(l1Output);
Logic2 l2 = new Logic2(logic2_inputs);
try{
Logic2Output l2Output = l2.execute();
} catch( Logic2Exception l2Exception) {
// exception handling
}
Logic3Inputs logic3_inputs = new Logic3Inputs(l1Output, l2Output);
Logic3 l3 = new Logic3(logic2_inputs);
try{
Logic3Output l3Output = l3.execute();
} catch( Logic3Exception l3Exception) {
// exception handling
}
} catch(GlobalException globalEx){
// exception handling
}
答案 0 :(得分:2)
我认为这称为管道:http://en.wikipedia.org/wiki/Pipeline_%28software%29此模式用于数据流经一系列任务或阶段的算法。
您可以搜索执行此操作的库(http://code.google.com/p/pipelinepattern)或尝试自己的java实现
基本上你在列表中拥有所有对象,并且一个si的输出传递给下一个。这是一个天真的实现,但您可以添加泛型和所有您需要的
public class BasicPipelinePattern {
List<Filter> filters;
public Object process(Object input) {
for (Filter c : filters) {
try {
input = c.apply(input);
} catch (Exception e) {
// exception handling
}
}
return input;
}
}
public interface Filter {
public Object apply(Object o);
}
答案 1 :(得分:1)
当遇到这样的问题时,我喜欢看其他编程语言如何解决它。然后我可以借用这个概念并将其应用到我正在使用的语言中。
在javascript中,有很多关于promises的讨论,以及它们如何简化异步处理和错误处理。 This page是对这个问题的一个很好的介绍。
然后使用“thenables”调用方法。这是伪代码:
initialStep.execute().then(function(result1){
return step2(result1);
}).then(function(result2){
return step3(result3);
}).error(function(error){
handle(error);
}).done(function(result3){
handleResult(result3)
});
这种模式的优势在于您可以专注于处理并在一个地方有效地处理错误,而无需担心在每一步检查是否成功。
那么这在java中如何运作?我想看看其中一个承诺/期货库,也许是jdeferred。我希望你能把这样的东西放在一起(假设java 8简洁):
initialPromise.then( result1 -> {
Logic2 logic2 = new Logic2(new Logic2Inputs(result1));
return logic2.execute();
}).then(result2 -> {
Logic3 logic3 = new Logic3(new Logic3Inputs(result2));
return logic2.execute();
}).catch(exception -> {
handleException(exception)
}).finally( result -> {
handleResult(result);
});
这当然掩盖了代码中隐藏的要求。您提到在步骤3中,您需要步骤1 和步骤2的输出。如果您正在编写scala,则会有语法糖来处理此问题(暂时不考虑错误处理) :
for(result1 <- initialStep.execute();
Logic2 logic2 = new Logic2(Logic2Input(result1));
result2 <- logic2.execute();
Logic3 logic3 = new Logic3(Logic3Input(result1, result2));
result3 <- logic3.execute()) yield result3;
但是由于你没有这里的能力,那么你可以选择重构每一步只采取上一步的输出,或者嵌套处理,以便在你需要时result1仍在范围内设置第3步。
这个经典的替代品,正如@ user1121883所提到的那样,将使用Pipeline处理器。这种方法的缺点是,如果您的输入和输出是相同类型,它最有效。否则你将不得不在任何地方推送Object并进行大量的类型检查。
另一种替代方法是为管道公开fluent interface。同样,你想要做一些重构,也许是为了让输入和输出具有无参数构造函数和一致的接口:
Pipeline p = new Pipeline();
p.then(new Logic1())
.then(new Logic2())
.then(new Logic3())
.addErrorHandlder(e->handleError(e))
.complete();
这最后一个选项是更具思想性的java,但保留了thenables处理的许多优点,所以它可能就是我要去的方式。