使用函式式實現命令模式
Command模式是物件式很容易轉換到函式式的一個模式,讓我們看一個簡單的例子來說明它是如何工作的。首先,有必要定義一個建模命令的介面:
<b>interface</b> Command { <b>void</b> run(); }
提供此Command介面的不同實現。例如,假設我們需要不同的命令來處理訊息 - 您可以使用一個命令來記錄訊息:
<b>public</b> <b>class</b> Logger implements Command { <b>public</b> <b>final</b> String message; <b>public</b> Logger( String message ) { <b>this</b>.message = message; } @Override <b>public</b> <b>void</b> run() { System.out.println(<font>"Logging: "</font><font> + message); } } </font>
將訊息儲存在檔案中是另外一個命令實現:
<b>public</b> <b>class</b> FileSaver implements Command { <b>public</b> <b>final</b> String message; <b>public</b> FileSaver( String message ) { <b>this</b>.message = message; } @Override <b>public</b> <b>void</b> run() { System.out.println(<font>"Saving: "</font><font> + message); } } </font>
電子郵件傳送:
<b>public</b> <b>class</b> Mailer implements Command { <b>public</b> <b>final</b> String message; <b>public</b> Mailer( String message ) { <b>this</b>.message = message; } @Override <b>public</b> <b>void</b> run() { System.out.println(<font>"Sending: "</font><font> + message); } } </font>
一個可以執行以多個命令的物件:
<b>public</b> <b>class</b> Executor { <b>public</b> <b>void</b> execute(List<Command> tasks) { <b>for</b> (Command task : tasks) { task.run(); } } }
最後可以將我們想要執行的命令新增到List中並通過Executor執行它們:
List<Command> tasks = <b>new</b> ArrayList<>(); tasks.add(<b>new</b> Logger( <font>"Hi"</font><font> )); tasks.add(<b>new</b> FileSaver( </font><font>"Cheers"</font><font> )); tasks.add(<b>new</b> Mailer( </font><font>"Bye"</font><font> )); <b>new</b> Executor().execute( tasks ); </font>
Gang of Four書中建議我們將函式(要執行的動作)包裝到物件(執行這些動作的命令)中,然而,這種間接級別除了允許我們將函式適合於嚴格面向物件的程式設計風格。但是,隨著Java 8中lambda的引入,現在可以無縫地混合函式和麵向物件的範例,我們可以以更緊湊的方式重新思考前一個例子。
首先,請注意我們不需要定義Command介面:Runnable,它存在於Java的第一個版本中,它有一個抽象方法,具有與Command 1相同的簽名。前3命令實現可以通過3個函式以更簡潔的方式替換,在Java 8中可以使用3個靜態方法實現:
<b>public</b> <b>static</b> <b>void</b> log(String message) { System.out.println(<font>"Logging: "</font><font> + message); } <b>public</b> <b>static</b> <b>void</b> save(String message) { System.out.println(</font><font>"Saving: "</font><font> + message); } <b>public</b> <b>static</b> <b>void</b> send(String message) { System.out.println(</font><font>"Sending: "</font><font> + message); } </font>
使用函式式思維重新思考命令實現帶來了顯著提高程式碼訊號/噪聲比的好處,其中訊號是函式體,而噪聲是用於表示該函式的所有附加程式碼作為方法一個東西。甚至Executor類也可以用單行靜態方法替換,將List of Runnables作為引數:
<b>public</b> <b>static</b> <b>void</b> execute(List<Runnable> tasks ) { tasks.forEach( Runnable::run ); }
我們的函式可以新增到List中,並像以前一樣執行。
List<Runnable> tasks = <b>new</b> ArrayList<>(); tasks.add(() -> log(<font>"Hi"</font><font>)); tasks.add(() -> save(</font><font>"Cheers"</font><font>)); tasks.add(() -> send(</font><font>"Bye"</font><font>)); execute( tasks ); </font>
Java編譯器自動轉換不帶引數的lambda,並呼叫void靜態方法,作為Runnable介面的匿名實現,從而讓Runnables型別的集合Collection中容納它們,而之前是需要實現Command介面的。