2014-10-08 20 views
4

在我的Java应用程序中,我得到了一个方法,它运行一系列长步骤(同步),其中一步结果是下一步的输入。没有通用接口的步骤序列的模式

例如:

// Step 1 
Map<String, SomeObject> objectsMap = someService.createObjectsMap(); 
if (!objectsMap.isEmpty()) { 
    // Step 2 
    AnotherObject anotherObject = anotherService.createAnotherObject(objectsMap); 
    if (null != anotherObject) { 
    // Step 3 that gets anotherObject as input and returns something else 
    } else { // Step 2 failed 
     // log and handle 
    } 
} else { // Step 1 failed 
    // log and handle 
} 

所以我得到了这个系列写了一系列的if-else块的步骤。 这些步骤没有通用接口,因为每个步骤都有不同的签名。我一直在讨厌一些不同,并试图自定义模式,如chain-of-responsibilitycommand,但无法达到满意的结果。

我不知道这个丑陋的长if-else部分是要走的路还是有一种模式可以帮助使这一系列步骤更加干净和可扩展。

+0

'try' /'catch'? – immibis 2014-10-08 06:17:20

+0

怎么样? – forhas 2014-10-08 06:17:55

+0

它似乎正是你想要的。 (好吧,你也需要抛出) – immibis 2014-10-08 06:27:05

回答

0

这种情况下 - 你做了很多事情,并希望中止并记录它们中的任何一个失败 - 是什么异常处理的设计。例如:

try { 
    // Step 1 
    Map<String, SomeObject> objectsMap = someService.createObjectsMap(); 
    if (objectsMap.isEmpty()) 
     throw new SomethingWentWrongException("Failed to get object map from service"); 

    // Step 2 
    AnotherObject anotherObject = anotherService.createAnotherObject(objectsMap); 
    if(anotherObject == null) 
     throw new SomethingWentWrongException("Failed to create another object"); 

    // Step 3 that gets anotherObject as input and returns something else 
} catch(SomethingWentWrongException e) { 
    // log and handle 
    e.printStackTrace(); 
} 

理想的情况下,someService.createObjectsMapanotherService.createAnotherObject会抛出让你检查返回值的自己的异常,而不是。然后你只需要编写:

try { 
    Map<String, SomeObject> objectsMap = someService.createObjectsMap(); 
    AnotherObject anotherObject = anotherService.createAnotherObject(objectsMap); 
    // Step 3 that gets anotherObject as input and returns something else 
} catch(Exception e) { 
    // log and handle 
    e.printStackTrace(); 
} 

(但请注意,您应该只赶上Exception,如果你真的想赶上所有失败),你必须回答自己

+1

我不明白这是如何改变的。而不是“其他”块你使用抛出声明,这将强制我建立一系列的例外(每一步将有它自己的例外)。通过这种方式,每一个新的步骤都需要添加,并且还应该创建额外的异常(如果你问我,也是不必要的)。另外,我的服务(从步骤中调用)不会抛出任何异常,try/catch机制很昂贵并且使事情变得更丑陋(如果你问我的话),我只需简单地对步骤结果进行验证检查not null) – forhas 2014-10-08 07:00:47

+0

@forhas您是否以不同方式处理每个故障情况? – immibis 2014-10-08 07:01:48

+0

让我们假设我这样做,但我可以很容易地验证我的结果,我提到我的服务在内部处理异常,并确保返回可以从顶级(运行步骤的地方)验证的答案 – forhas 2014-10-08 07:46:44

5

一个问题是为什么我想重构我的代码?

你希望它是

  • 更干净的代码?
  • 更模块化?
  • 步骤必须在运行时可配置(可替换)吗?

重构,以使代码干净

如果步骤不需要在运行时进行配置,你想使你的代码更干净的比你应该看一看的评论你制作。每条评论都是一个提示。

步骤

/** 
* Explain what step1 does. 
*/ 
private void step1() { 
    // Step 1 
    Map<String, SomeObject> objectsMap = someService.createObjectsMap(); 
    if (!objectsMap.isEmpty()) { 
     step2(objectsMap); 
    } else { // Step 1 failed 
     // log and handle 
    } 
} 

/** 
* Explain what step2 does. 
*/ 
private void step2(Map<String, SomeObject> objectsMap) { 
    // Step 2 
    final AnotherObject anotherObject = anotherService 
      .createAnotherObject(objectsMap); 
    if (null != anotherObject) { 
     step3(anotherObject); 
    } else { // Step 2 failed 
     // log and handle 
    } 
} 

/** 
* Explain what step3 does. 
*/ 
private void step3(AnotherObject anotherObject) { 
    // Step 3 that gets anotherObject as input and returns something 
    // else 
} 

这种方法只是分解成更小的方法的方法后,打破代码块到方法,并将它们命名。优点是每个较小的方法只对一件事负责。而且因为这是一种方法,所以可以将javadoc添加到它。所以不再需要内嵌评论。

为了使步骤在运行时

更换如果你想配置比你必须封装它们中的对象在运行时(例如,由于一些用户输入的)执行的步骤,因为重构你的应用程序引用了可以替换的对象。

既然你想要所有的步骤都有一个共同的API,你必须使它更通用。

从客户的角度开始思考。这些步骤应该如何执行。例如。

for (Step step : steps) { 
    boolean executeNext = step.execute(); 
    if (!executeNext) { 
     break; 
    } 
} 

设计一个步骤接口

public interface Step { 
    boolean execute(); 
} 

如何通过一个步骤作为输入到另一个的输出?

做一个接口

public static interface StepInput<T> { 
    public T getInput(); 
} 

实现你的脚步。抽象类将帮助你。

public abstract class InputOutputStep<T> implements Step, 
     StepInput<T> { 

    private T returnValue; 

    protected void setReturnValue(T returnValue) { 
     this.returnValue = returnValue; 
    } 

    public T getInput() { 
     return returnValue; 
    } 
} 

public class Step1 extends InputOutputStep<Map<String, SomeObject>> { 

    private StepInput<Map<String, SomeObject>> stepInput; 

    public Step1(StepInput<Map<String, SomeObject>> stepInput) { 
     this.stepInput = stepInput; 
    } 

    public boolean execute() { 
     boolean executeNext = false; 

     Map<String, SomeObject> objectsMap = stepInput.getInput(); 
     if (!objectsMap.isEmpty()) { 
      // Step 2 
      setReturnValue(objectsMap); 
      executeNext = true; 
     } else { // Step 1 failed 
      // log and handle 
     } 

     return executeNext; 
    } 
} 

public class Step2 extends InputOutputStep<AnotherObject> { 

    private StepInput<Map<String, SomeObject>> stepInput; 
    private AnotherService anotherService; 

    public Step2(AnotherService anotherService, 
      StepInput<Map<String, SomeObject>> stepInput) { 
     this.anotherService = anotherService; 
     this.stepInput = stepInput; 
    } 

    public boolean execute() { 
     boolean executeNext = false; 

     Map<String, SomeObject> objectsMap = stepInput.getInput(); 
     AnotherObject anotherObject = anotherService 
       .createAnotherObject(objectsMap); 
     if (null != anotherObject) { 
      setReturnValue(anotherObject); 
      executeNext = true; 
     } else { // Step 2 failed 
      // log and handle 
     } 
     return executeNext; 
    } 
} 

public class Step3 extends InputOutputStep<Void> { 

    private StepInput<AnotherObject> stepInput; 

    public Step3(StepInput<AnotherObject> stepInput) { 
     this.stepInput = stepInput; 
    } 

    public boolean execute() { 
     AnotherObject anotherObject = stepInput.getInput(); 
     setReturnValue(null); 
     return false; 
    } 
} 

在运行时配置的步骤和执行

Step1 step1 = new Step1(stepInput); 
Step2 step2 = new Step2(anotherService, step1); 
Step step3 = new Step3(step2); 

Step[] steps = new Step[]{step1, step2, step3}; 

for (Step step : steps) { 
    boolean executeNext = step.execute(); 
    if (!executeNext) { 
     break; 
    } 
} 
+0

我喜欢您的解决方案(与重构相关的解决方案,以便在运行时更换步骤),并确保尽快尝试。我确实尝试了类似的东西,但是你有几点我似乎错过了。 – forhas 2014-10-08 08:16:29

+0

我有这个解决方案的问题。看起来T代表两个 - 一个步骤的输入和输出,但这没有意义。在抽象类InputOutputStep中,两个方法都引用同一个成员。也许应该有2个泛型类型成员(用于输入和输出),但这或多或少是因为我在发布没有成功的问题之前试图做的事情 – forhas 2014-10-08 15:27:54

+0

要连接两个步骤,必须生成一个输出另一个。所以'T'表示另一个用作输入的一个步骤的输出。你可能会感到困惑,你用'T'类型调用'setReturnValue'。但正如上面所解释的,'T'必须是为了连接两个步骤。我希望我能让自己的想法更加清晰。 – 2014-10-09 06:18:25

-1

选项:

  1. 备忘录模式: 有了纪念品,都可以存储对象的状态。就像当你想重做和撤销东西时一样。 这样,当您在步骤1和步骤3中有类似的方法时,您可以简单地使用1个一般方法。 然后,通过撤消和重做,你知道你必须保存你的状态。 想想保存你的步数的可能性。

  2. 策略模式: 使用策略模式,您正在保存IF-ELSE语句。 你只需要去一个功能,策略对象将决定其余的。 将路由器视为策略类。 路由器将确定最佳方式,最佳路径或最佳过程(来自多个过程选项)。

  3. 观察者模式: 这就像MVC。 我一直认为观察员是中央电视台。 当一些事情发生变化时,一些奇怪的事情发生了,央视管理员会知道。 所以你有一个控制器类,它监控所有事情,并配置你下一步要去的地方。

谢谢

+1

你的答案纯粹是理论上的,与问题无关。 – forhas 2014-10-08 13:22:59

+0

http://www.dofactory.com/net/design-patterns – Jedi 2014-10-09 05:31:21

+1

你并没有变得更好..如果你对我的具体问题有一个答案,那么欢迎大家光临,但如果你只是想扔掉空气中的一些图案和波浪一些书请让我免费。 – forhas 2014-10-09 16:02:17