考察:やりたいことは「何か起きたら知らせてください」
以上の例より、オブザーバオブジェクトは、ラムダ式によって簡潔に生成できることが分かります。
前々回に紹介したCommandパターンの場合、前回に紹介したStrategyパターンの場合と同様に、ラムダ式を使うプログラム(リスト3)は、Observerパターンのプログラム(リスト2)から、構造は大きく変わっていません。実質的な差異は、ジョブオブザーバを生成するところで、ラムダ式を使っているということだけです。したがって、リスト3のプログラムもやはり、Observerパターンを採用しているとみなすことは可能です。
しかしながら、リスト3の main メソッドで addObserver メソッドを呼び出しているところを読んで、わざわざ「ConcreteSubjectであるジョブ定義クラスのインスタンスにオブザーバオブジェクトを登録する」と理解する必要は薄いように思われます。ここで表現されていることは、「ジョブに何かが起きたらが知らせてください、とジョブ定義に頼む」というだけのことだからです。ラムダ式によって「知らせを受ける処理」がプログラムの式の中で表せるようになったため、あえて「オブザーバオブジェクト」のような概念を導入する必要が薄まった、といえるでしょう。
「何かが起きたら知らせてください」という意図を素直に書けるようにするためには、 「 addObserver 」というメソッド名も再考したほうがよいかもしれません。例えば、リスト4のように 「 onJobUpdate 」というメソッド名であれば、「ジョブに何かが起きたら~」という気持ちが分かりやすくなるように思われます。
// バックアップジョブの状態に変更があったらログ出力する
backupJobDefinition.onJobUpdate(backupJob -> {
System.out.printf("[INFO] バックアップ: %s ステータス: %s%n",
backupJob.getFileName(), backupJob.getStatus());
});
念のために申し添えておくと、インタフェースのメソッド名の変更は、既存のプログラムを壊さないことを確かめながら、慎重に行われる必要があります。
Observerの役割を果たすインタフェースは、関数的インタフェースであることが多いため、ジョブコントローラプログラムに限らず、そのインスタンス生成にラムダ式が使える可能性があります。例えば、リスト1で紹介したJavaFXの ChangeListener インタフェースも関数型インタフェースです。したがって、リスト1のプログラムは、ラムダ式を使ってリスト5のように書き換えられます。
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
public class LambdaWindowSizeMonitor extends Application {
/** 表示領域. */
private final BorderPane pane = new BorderPane();
/** ウィンドウのシーン. */
private final Scene scene = new Scene(this.pane, 600, 400);
/**
* 表示領域にウィンドウサイズを表示する.
*/
private void showSize() {
String message = String.format("%s x %s", scene.getWidth(), scene.getHeight());
pane.setCenter(new Label(message));
}
@Override
public void start(Stage stage) {
showSize();
// ウィンドウサイズが変わったらサイズを表示し直す
scene.widthProperty().addListener((prop, oldNum, newNum) -> showSize());
scene.heightProperty().addListener((prop, oldNum, newNum) -> showSize());
stage.setScene(scene);
stage.setTitle("Lambda Window Size Monitor");
stage.show();
}
public static void main(String[] args) {
launch(LambdaWindowSizeMonitor.class, args);
}
}
リスト5では、ウィンドウサイズ変更の通知を受けて showSize メソッドを呼び出すオブザーバオブジェクトを、ラムダ式で生成して addListener メソッドに渡しています。「ウィンドウサイズが変わったら知らせてください、showSize メソッドでサイズを表示し直すから」という意図がより分かりやすく表現できているのではないでしょうか。
おわりに
今回使ったジョブコントローラプログラムは、ジョブ定義がジョブを生成する箇所で、Template MethodパターンとFactory Methodパターンを組み合わせて使っています。この組み合わせは、現実のアプリケーションでもよく登場するものです。
連載最終回となる次回は、今回のジョブコントローラプログラムを題材として、Template MethodパターンとFactory Methodパターンのラムダ式による書き換えを取り上げます。これまでに扱ったCommandパターン、Strategyパターン、Observerパターンと異なり、比較的大規模なプログラム構造の変更が必要となります。この書き換えを通じて、ラムダ式が活用できるようなプログラムの構造について理解が深められるはずです。
それでは、最終回もお楽しみに。
