使用函數語言程式設計重構模板模式
為了實際說明模板模式在哪些情況下有用,我們假設我們有一個類如下的Resource類:
public class Resource { public Resource() { System.out.println("Resource created"); } public void useResource() { riskyOperation(); System.out.println("Resource used"); } public void employResource() { riskyOperation(); System.out.println("Resource employed"); } public void dispose() { System.out.println("Resource disposed"); } private void riskyOperation() { if ( new Random().nextInt( 10 ) == 0) { throw new RuntimeException(); } } } |
我們的Resouce有一個建構函式,一些訪問它的方法以及另一個在不再使用該物件時如何處理它的方法,這些方法可能會像使用資料庫或網路連線那樣進行冒險操作,然後最終可能會失敗丟擲RuntimeException。這裡透過從10中隨機丟擲異常來模擬失敗的可能性。無論這些方法呼叫的結果如何,在完成使用資源之後呼叫dispose()是強制性的,以便釋放連線和其他使用了工件,從而避免了記憶體,連線檔案指標洩漏。在這種情況下,使用資源如下...
Resource resource = new Resource(); resource.useResource(); resource.employResource(); resource.dispose(); |
這是錯誤的, 因為useResoure()或employResource()方法可能最終丟擲異常,然後阻止正確處理資源。顯然,使用此Resouce的正確方法是這樣的:
Resource resource = new Resource(); try { resource.useResource(); resource.employResource(); } finally { resource.dispose(); } |
問題是我們無法保證每次使用我們的資源都會按照這種模式正確處理。我們不希望冒被資源被濫用的可能性 - 或者我們希望提供一個API,強制Resource物件的客戶端始終處置它。換句話說,我們希望確保資源的客戶端始終按照以下模式使用它:
openResource(); try { doSomethingWithResource(); } finally { closeResource(); } |
這裡我們剛剛定義了一個程式碼模板,然後我們可以將它放在一個抽象類中:
public abstract class AbstractResourceManipulatorTemplate { protected Resource resource; private void openResource() { resource = new Resource(); } protected abstract void doSomethingWithResource(); private void closeResource() { resource.dispose(); resource = null; } public void execute() { openResource(); try { doSomethingWithResource(); } finally { closeResource(); } } } |
此抽象模板封裝了我們要強制執行的使用模式,並提供了一種抽象方法,其中不同的具體實現可以定義如何與Resource進行互動。
public class ResourceUser extends AbstractResourceManipulatorTemplate { @Override protected void doSomethingWithResource() { resource.useResource(); } } public class ResourceEmployer extends AbstractResourceManipulatorTemplate { @Override protected void doSomethingWithResource() { resource.employResource(); } } |
透過這種方式,在這些具體實現上呼叫execute()會在Resource上執行各自doSomethingWithResource()方法體中定義的操作,同時確保每次使用它的資源也將被正確處理。
new ResourceUser().execute(); new ResourceEmployer().execute(); |
提供模板來強制我們的資源的客戶端以正確的方式使用它的想法是正確的,但正如我們已經看到的其他模式,GoF書中描述的純OOP實現是冗長和繁瑣的。使用單個方法接受資源消費者,可以直接獲得相同的結果。
public static void withResource( Consumer<Resource> consumer) { Resource resource = new Resource(); try { consumer.accept( resource ); } finally { resource.dispose(); } } |
請注意,此方法的主體等同於抽象模板的execute()方法所做的,唯一的區別是模板的抽象方法提供的自由度現在透過傳遞給withResouce()方法的Consumer實現。 。然後,可以用簡單的lambda表示式替換定義如何使用Resource的具體模板實現。
withResource( resource -> resource.useResource() ); withResource( resource -> resource.employResource() ); |
相關文章
- 函數語言程式設計下的Iterator模式函數程式設計模式
- 重識Java8函數語言程式設計Java函數程式設計
- 函數語言程式設計函數程式設計
- 函數語言程式設計讓你忘記設計模式函數程式設計設計模式
- Scala 函數語言程式設計(一) 什麼是函數語言程式設計?函數程式設計
- 由重構react元件引發的函數語言程式設計的思考React元件函數程式設計
- 函數語言程式設計,真香函數程式設計
- JavaScript 函數語言程式設計JavaScript函數程式設計
- Java 函數語言程式設計Java函數程式設計
- javascript函數語言程式設計JavaScript函數程式設計
- 初探函數語言程式設計函數程式設計
- 函數語言程式設計初探函數程式設計
- 使用 Go 泛型的函數語言程式設計Go泛型函數程式設計
- 使用 JavaScript 進行函數語言程式設計 (一)JavaScript函數程式設計
- 使用JavaScript實現“真·函數語言程式設計”JavaScript函數程式設計
- 函數語言程式設計雜談函數程式設計
- 初見函數語言程式設計函數程式設計
- RAC的函數語言程式設計函數程式設計
- JavaScript 函數語言程式設計(二)JavaScript函數程式設計
- 函數語言程式設計前菜函數程式設計
- JavaScript 函數語言程式設計(一)JavaScript函數程式設計
- JavaScript 函數語言程式設計(三)JavaScript函數程式設計
- python函數語言程式設計Python函數程式設計
- JavaScript函數語言程式設計(二)JavaScript函數程式設計
- JavaScript函數語言程式設計(一)JavaScript函數程式設計
- JavaScript函數語言程式設計(三)JavaScript函數程式設計
- 函數語言程式設計初探一函數程式設計
- iOS 與 函數語言程式設計iOS函數程式設計
- 函數語言程式設計簡介函數程式設計
- 函數語言程式設計入門函數程式設計
- C#函數語言程式設計C#函數程式設計
- Swift の 函數語言程式設計Swift函數程式設計
- Scala函式與函數語言程式設計函式函數程式設計
- .NET併發程式設計-函數語言程式設計程式設計函數
- 函數語言程式設計-鏈式程式設計RAC函數程式設計
- 使用 Java 8 函數語言程式設計生成字母序列Java函數程式設計
- 使用JavaScript實現“真·函數語言程式設計”-2JavaScript函數程式設計
- javascript函數語言程式設計: 優雅的使用underscore進行函式程式設計JavaScript函數程式設計函式