【问题标题】:Design pattern for incremental code增量代码的设计模式
【发布时间】:2015-06-09 12:32:46
【问题描述】:

根据业务逻辑,一个方法的输出被用作另一个方法的输入。逻辑具有线性流动。 为了模拟这种行为,现在有一个控制器类包含所有内容。

它非常混乱,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
}

【问题讨论】:

  • @user1121883 我认为您的评论(好吧,除了纯链接之外还有一些其他作品)会是一个很好的答案!
  • 就目前而言,我不认为代码太糟糕了。事实上,我可以想象规范看起来和代码非常相似,如果规范很复杂,真的没什么可做的。话虽如此,您当然可以随时将其分解为更小的方法,并在适当的地方添加 cmets。
  • 责任链的一些变体也可以工作

标签: java design-patterns


【解决方案1】:

我认为这称为管道: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);
}

【讨论】:

  • BasicPipelinePattern 可以实现 Component 如果您将 process 重命名为 apply。这意味着使用它的代码不知道它是单个 Component、整个管道还是复杂的组件树。
【解决方案2】:

当遇到这样的问题时,我想看看其他编程语言如何解决它。然后我可能会借用这个概念并将其应用到我正在使用的语言中。

在 javascript 中,有很多关于 Promise 的讨论,以及它们如何不仅可以简化异步处理,还可以简化错误处理。 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 中是如何工作的呢?我会看看其中一个 promise/futures 库,也许是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 提到的那样,经典的替代方法是使用管道处理器。这种方法的缺点是,如果您的输入和输出类型相同,则效果最佳。否则你将不得不到处推送 Object 并进行大量类型检查。

另一种选择是为管道公开fluent interface。同样,您需要进行一些重构,也许是为了拥有一个无参数的构造函数和一个一致的输入和输出接口:

Pipeline p = new Pipeline();
p.then(new Logic1())
 .then(new Logic2())
 .then(new Logic3())
 .addErrorHandlder(e->handleError(e))
 .complete();

这最后一个选项更符合 java 的概念,但保留了 thenables 处理的许多优点,所以这可能是我要走的路。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-18
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多