1. 程式人生 > >用Java 8 Lambda表示式實現設計模式:命令模式

用Java 8 Lambda表示式實現設計模式:命令模式

在這篇部落格裡,我將說明如何在使用 Java 8 Lambda表示式 的函數語言程式設計方式 時實現 命令 設計模式 。命令模式的目標是將請求封裝成一個物件,從對客戶端的不同型別請求,例如佇列或日誌請求引數化,並提供相應的操作。命令模式是一種通用程式設計方式,該方式基於執行時決策順序來執行方法。模式的參與者如下:

命令   : 宣告用於執行操作的介面。
實體命令 :定義接收者物件和動作的繫結。
客戶端 :建立實體命令例項並設定它的接收者。
呼叫者: 控制命令來執行請求。
接收者 :實際完成工作。

這些參與者之間的關係描述如下:

1.jpg

讓我們看一個命令模式的具體例子,瞭解它是如何轉換成lambda表示式的。假定我們有一個檔案系統工具,所有動作都依賴它,例如開啟檔案,向檔案寫入和關閉檔案。這可以實現巨集功能 ,即一系列的操作可以被記錄下來,之後作為一個單獨操作執行。下面是我們的接收者。

public interface FileSystemReceiver {
  void openFile();
  void writeFile();
        void closeFile();
}
每個操作都是命令,例如 openFile 和 writeFile

。我們可以建立一個通用命令介面來適配這些不同的操作。讓我們將介面命名為Action,因為在我們的情境下,它代表執行一個操作。所有命令物件都需要實現這個介面。

public interface Action {
    public void perform();
}
現在,讓我們為每個操作實現Action介面。所有這些類需要做的就是呼叫FileReceiver的一個方法,並將這個呼叫封裝到Action介面中。在封裝操作後,使用適當的類命名規範來命名這些類 。 因此,openFile方法物件的類稱為 OpenFile 。

public class OpenFile implements Action {
  private final FileReceiver fileReceiver;
  public OpenFile(FileReceiver fileReceiver) {
    this.fileReceiver = fileReceiver;
  }
  public void perform() {
    fileReceiver.openFile();
  }
}
現在,我們實現 Macro 類。每個巨集包含一個動作序列,該序列可以依次執行動作,它將作為呼叫者。這個類可以記錄動作,並一起執行。我們可以將動作序列儲存在列表中,然後反覆地獲取每個動作來執行。
public class Macro {
  private final List actions;
  public Macro() {
    actions = new ArrayList<>();
  }
  public void record(Action action) {
    actions.add(action);
  }
  public void run() {
    actions.forEach(Action::perform);
  }
}

當填充巨集物件時,我們可以新增每個命令的例項,這些例項也會被記錄在巨集物件裡。現在簡單的執行巨集物件,每個命令將依次執行。我們的客戶端程式碼如下。
Macro macro = new Macro();
macro.record(new OpenFile(fileReceiver));
macro.record(new WriteFile(fileReceiver));
macro.record(new CloseFile(fileReceiver));
macro.run();

如果你跟著我的思路讀到這裡,你會好奇lambda表示式如何適配這些的。實際上,所有命令類,例如OpenFile、WriteFile和CloseFile,其實只是lambda表示式想打破它們的封裝。這些命令類只是在類之間傳遞。使用lambda表示式整個模式得到大大的簡化,因為我們完全可以廢除這些類。然我們看看巨集類(客戶端)使用lambda表示式代替命令類的效果。
Macro macro = new Macro();
macro.record(() -> fileReceiver.openFile());
macro.record(() -> fileReceiver.writeFile());
macro.record(() -> fileReceiver.closeFile());
macro.run();
如果能夠意識到每個lambda表示式都在執行一個單獨的方法呼叫,可以進一步改進。因此,可以直接使用方法引用。
Macro macro = new Macro();
macro.record(fileReceiver::openFile);
macro.record(fileReceiver::writeFile);
macro.record(fileReceiver::closeFile);
macro.run();
命令模式易於擴充套件,新的動作方法可以被新增到接收者中來建立新的命令實現,這樣不需要改變任何客戶端程式碼。JDK中的 Runnable 介面(java.lang.Runnable)是命令模式常用的流行介面。這篇部落格中,我試著闡述帶Java 8 lambda表示式的命令模式。你可以看到使用lambda表示式,少了很多樣板程式碼,從而讓程式碼變得更整潔。