ジョブコントローラ(ラムダ式バージョン)
それでは次に、Java SE 8のラムダ式を用いてジョブコントローラのプログラムを書き直してみましょう。
リスト2の中で、Observerの役割を担う JobObserver インタフェースは、update メソッドを唯一の抽象メソッドとして持つ関数型インタフェースです。したがって、JobObserver インタフェースのインスタンスは、LogBackupJobObserver クラスや MailBackupJobObserver クラスのようなConcreteObserverの役割を担うクラスを宣言することなく、ラムダ式によって直接生成できます。
この方針に基づいて、ラムダ式を用いてプログラムを設計した場合のクラス図は、図6のとおりです。
ラムダ式を用いてリスト2を書き換えたプログラムを、リスト3に掲げます。リスト2からの変更点は「★」印で示しています。
import java.util.List;
import java.util.ArrayList;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* ジョブ定義 (Subjectその1).
*/
abstract class JobDefinition<J extends Job> {
/** オブザーバ一式. */
private final List<JobObserver<? super J>> observers = new ArrayList<>();
/** オブザーバを登録. */
public void addObserver(JobObserver<? super J> observer) {
this.observers.add(observer);
}
/** ジョブを作成、実行. */
public final void execute() {
J job = makeJob();
notifyObservers(job);
job.execute();
notifyObservers(job);
}
/** オブザーバにジョブの状態を通知. */
private void notifyObservers(J job) {
for (JobObserver<? super J> observer : this.observers) {
observer.update(job);
}
}
/** ジョブを作成するファクトリメソッド. */
abstract protected J makeJob();
}
/**
* ジョブ (Subjectその2).
*/
abstract class Job {
/** ジョブの状態. */
enum Status { UNFINISHED, FINISHED }
/** ジョブの状態. */
private Status status;
Job() {
this.status = Status.UNFINISHED;
}
/** ジョブの状態を戻す. */
final Status getStatus() {
return this.status;
}
/** ジョブが完了していたらtrueを戻す. */
final boolean isFinished() {
return this.status == Status.FINISHED;
}
/** ジョブを実行. */
final void execute() {
run();
this.status = Status.FINISHED;
}
/** ジョブの処理の内容を実装. */
abstract void run();
}
/**
* ジョブ実行のオブザーバ (Observer)
*
* ★関数型インタフェースであることを明示.
*/
@FunctionalInterface
interface JobObserver<J extends Job> {
void update(J job);
}
public class JobController2 {
/**
* データベースをバックアップするジョブの定義 (ConcreteSubjectその1).
*/
static class BackupJobDefinition extends JobDefinition<BackupJob> {
/** バックアップファイル名のフォーマッタ. */
private static final DateTimeFormatter FILE_NAME_FORMATTER
= DateTimeFormatter.ofPattern("'/backup/'yyyy_MM_dd'.sql'");
@Override
protected BackupJob makeJob() {
LocalDate startDate = LocalDate.now();
String fileName = startDate.format(FILE_NAME_FORMATTER);
return new BackupJob(fileName);
}
}
/**
* データベースをバックアップするジョブ (ConcreteSubjectその2).
*/
static class BackupJob extends Job {
/** バックアップファイルの名前. */
private final String fileName;
BackupJob(String fileName) {
this.fileName = fileName;
}
/** バックアップファイルの名前を戻す. */
String getFileName() {
return this.fileName;
}
@Override
void run() {
System.out.printf("$ pg_dumpall >%s%n", this.fileName);
}
}
public static void main(String[] args) {
// バックアップジョブ定義
BackupJobDefinition backupJobDefinition = new BackupJobDefinition();
// バックアップジョブの状態をログ出力するオブザーバをラムダ式で登録★
backupJobDefinition.addObserver(backupJob -> {
System.out.printf("[INFO] バックアップ: %s ステータス: %s%n",
backupJob.getFileName(), backupJob.getStatus());
});
// バックアップジョブ完了をメール通知するオブザーバをラムダ式で登録★
String admin = "eva.lu.ator@example.org";
backupJobDefinition.addObserver(backupJob -> {
if (backupJob.isFinished()) {
System.out.printf("$ echo '%sへのバックアップ完了!' | mail -s 'バックアップ通知' %s%n",
backupJob.getFileName(), admin);
}
});
// バックアップジョブ実行
backupJobDefinition.execute();
}
}
リスト3のプログラムの実行結果は次のとおりです。リスト2の実行結果と正確に同じです。
[INFO] バックアップ: /backup/2015_02_11.sql ステータス: UNFINISHED $ pg_dumpall >/backup/2015_02_11.sql [INFO] バックアップ: /backup/2015_02_11.sql ステータス: FINISHED $ echo '/backup/2015_02_11.sqlへのバックアップ完了!' | mail -s 'バックアップ通知' eva.lu.ator@example.org
リスト3では、main メソッド内で JobDefinition クラスの addObserver メソッドを呼び出しているところで、引数のジョブオブザーバを作るためにラムダ式を使っています。ラムダ式を使うために不要になった LogBackupJobObserver クラスと MailBackupJobObserver クラスは削除されています。

