SHOEISHA iD

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

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

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

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

ラムダ式でTemplate Methodパターン+Factory Methodパターンで実装されたコードをシンプルにする ~ 継承から委譲、そして単なる手続きへ

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

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

 リスト3のプログラムでAbstractFactoryの役割を担っている Supplier インタフェースは、先にも述べたように、関数型インタフェースです。したがって、Supplier インタフェースのインスタンスは、BackupJobFactory クラスのようなクラスを介さずに、ラムダ式で直接生成できます。このように変更したプログラムは、図7のような構成になります。

図7: ジョブコントローラプログラム(ファクトリをラムダ式に)
図7: ジョブコントローラプログラム(ファクトリをラムダ式に)

 

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

リスト4: JobController4.java
import java.util.List;
import java.util.ArrayList;
import java.util.function.Supplier;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
 * ジョブ定義 (Client).
 */
class JobDefinition<J extends Job> {

    /** ジョブのファクトリ. */
    private final Supplier<? extends J> jobMaker;

    /** オブザーバ一式. */
    private final List<JobObserver<? super J>> observers = new ArrayList<>();

    public JobDefinition(Supplier<? extends J> jobMaker) {
        this.jobMaker = jobMaker;
    }

    /** オブザーバを登録. */
    public void addObserver(JobObserver<? super J> observer) {
        this.observers.add(observer);
    }

    /** ジョブを作成、実行. */
    public final void execute() {
        J job = this.jobMaker.get();
        notifyObservers(job);
        job.execute();
        notifyObservers(job);
    }

    /** オブザーバにジョブの状態を通知. */
    private void notifyObservers(J job) {
        for (JobObserver<? super J> observer : this.observers) {
            observer.update(job);
        }
    }
}

/**
 * ジョブ (AbstractProduct).
 */
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();
}

/**
 * ジョブ実行のオブザーバ.
 */
@FunctionalInterface
interface JobObserver<J extends Job> {
    void update(J job);
}

public class JobController4 {

    /**
     * データベースをバックアップするジョブ (ConcreteProduct).
     */
    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);
        }
    }

    /** バックアップファイル名のフォーマッタ. */
    private static final DateTimeFormatter FILE_NAME_FORMATTER
        = DateTimeFormatter.ofPattern("'/backup/'yyyy_MM_dd'.sql'");

    public static void main(String[] args) {
        // バックアップジョブの生成をラムダ式で登録★
        JobDefinition<BackupJob> backupJobDefinition = new JobDefinition<>(() -> {
            LocalDate startDate = LocalDate.now();
            String fileName = startDate.format(FILE_NAME_FORMATTER);
            return new BackupJob(fileName);
        });

        // バックアップジョブの状態をログ出力するオブザーバを登録
        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とまったく同一です。

[INFO] バックアップ: /backup/2015_03_12.sql ステータス: UNFINISHED
$ pg_dumpall >/backup/2015_03_12.sql
[INFO] バックアップ: /backup/2015_03_12.sql ステータス: FINISHED
$ echo '/backup/2015_03_12.sqlへのバックアップ完了!' | mail -s 'バックアップ通知' eva.lu.ator@example.org

次のページ
考察:継承から委譲、そして単なる手続きへ

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

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

もっと読む

この記事の著者

宮川 拓(ミヤカワ タク)

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8571 2015/03/27 14:00

イベント

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

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

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

メールバックナンバー