2014-10-08 23 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