ジョブコントローラ(Observerパターン)
それではここから、簡易的なジョブコントローラを題材として、Observerパターンのプログラムが、ラムダ式を使ってどのように書き直せるかを見ていきます。
このジョブコントローラでは、主に次の役割が登場します。
- ジョブ定義:ジョブを生成、実行する。ジョブの状態が変わった時に、登録されたジョブオブザーバに通知する
- ジョブ:バッチ処理を行う。未実行/実行済みの状態を持つ
- ジョブオブザーバ:メール送付やログ出力など、ジョブの状態に対応する処理を行う
まずは、Observerパターンを使って実装した場合のプログラムを見てみましょう。プログラムのクラス図は図4のとおりです。
それぞれのクラス、インタフェースの役割は次のとおりです。
- JobDefinition:ジョブ定義の抽象クラス。Subjectの役割の一部を担い、ジョブオブザーバの登録・通知を担う
- BackupJobDefinition:バックアップジョブのジョブ定義クラス。ConcreteSubjectの役割の一部を担う
- Job:ジョブの抽象クラス。Subjectの役割の一部を担い、状態の保持と変更を担う
- BackupJob:バックアップジョブのクラス。ConcreteSubjectの役割の一部を担う
- JobObserver:ジョブオブザーバのインタフェース。Observerの役割を担う
- LogBackupJobObserver:ジョブの状態をログ出力するジョブオブザーバ。ConcreteObserverの役割を担う
- MailBackupJobObserver:ジョブ完了のメールを送付するジョブオブザーバ。ConcreteObserverの役割を担う
また、Observerパターンを用いたプログラムのシーケンス図は図5のとおりです(主要なクラス・メソッドのみ記載しています)。
JobController1 クラスは、BackupJobDefinition クラスのジョブ定義にジョブオブザーバを登録します。BackupJobDefinition クラスの execute メソッドが呼ばれると、BackupJob クラスのジョブを作成し、その execute メソッドを呼び出します。ジョブの execute メソッドを呼ぶ前後に、ジョブオブザーバに状態変更が通知されます。
Observerパターンを用いたプログラムをリスト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)
*/
interface JobObserver<J extends Job> {
void update(J job);
}
public class JobController1 {
/**
* データベースをバックアップするジョブの定義 (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);
}
}
/**
* バックアップジョブ完了をログ出力するオブザーバ.
*/
static class LogBackupJobObserver implements JobObserver<BackupJob> {
@Override
public void update(BackupJob backupJob) {
System.out.printf("[INFO] バックアップ: %s ステータス: %s%n",
backupJob.getFileName(), backupJob.getStatus());
}
}
/**
* バックアップジョブ完了をメール通知するオブザーバ.
*/
static class MailBackupJobObserver implements JobObserver<BackupJob> {
/** 通知先メールアドレス. */
private final String mailAddress;
MailBackupJobObserver(String mailAddress) {
this.mailAddress = mailAddress;
}
@Override
public void update(BackupJob backupJob) {
if (backupJob.isFinished()) {
System.out.printf("$ echo '%sへのバックアップ完了!' | mail -s 'バックアップ通知' %s%n",
backupJob.getFileName(), this.mailAddress);
}
}
}
public static void main(String[] args) {
// バックアップジョブ定義
BackupJobDefinition backupJobDefinition = new BackupJobDefinition();
// バックアップジョブの状態をログ出力するオブザーバを登録
backupJobDefinition.addObserver(new LogBackupJobObserver());
// バックアップジョブ完了をメール通知するオブザーバを登録
String admin = "eva.lu.ator@example.org";
backupJobDefinition.addObserver(new MailBackupJobObserver(admin));
// バックアップジョブ実行
backupJobDefinition.execute();
}
}
実行結果は次のとおりです。
[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
BackupJobDefinition クラスや BackupJob クラスの中では、ジョブ状態変更を受けてのログ出力やメール送信の処理を記述することなく、オブザーバオブジェクトに託しています。

