SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング

ラムダ式でObserverパターンで実装されたコードをシンプルにする ~ オブザーバオブジェクトはラムダ式によって簡潔に生成できる

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング 第3回


ジョブコントローラ(ラムダ式バージョン)

 それでは次に、Java SE 8のラムダ式を用いてジョブコントローラのプログラムを書き直してみましょう。

 リスト2の中で、Observerの役割を担う JobObserver インタフェースは、update メソッドを唯一の抽象メソッドとして持つ関数型インタフェースです。したがって、JobObserver インタフェースのインスタンスは、LogBackupJobObserver クラスや MailBackupJobObserver クラスのようなConcreteObserverの役割を担うクラスを宣言することなく、ラムダ式によって直接生成できます。

 この方針に基づいて、ラムダ式を用いてプログラムを設計した場合のクラス図は、図6のとおりです。

図6 ジョブコントローラプログラムのシーケンス図(ラムダ式)
図6 ジョブコントローラプログラムのシーケンス図(ラムダ式)

 

 ラムダ式を用いてリスト2を書き換えたプログラムを、リスト3に掲げます。リスト2からの変更点は「★」印で示しています。

リスト3:JobController2.java ~ ラムダ式を用いたジョブコントローラプログラム
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 クラスは削除されています。

次のページ
考察:やりたいことは「何か起きたら知らせてください」

この記事は参考になりましたか?

デザインパターンを置き換えよう! Javaラムダ式によるシンプルコーディング連載記事一覧

もっと読む

この記事の著者

宮川 拓(ミヤカワ タク)

日本Javaユーザーグループ幹事。東京のシステムインテグレータに勤務。Java VM上で動作する言語である「Kink」を開発中。相撲とアメリカ文学とスコティッシュポップを愛する。 ・ブログ: http://d.hatena.ne.jp/miyakawa_taku/ ・Twitter: @miyakawa_taku

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8504 2015/03/12 21:10

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー