SHOEISHA iD

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

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

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

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

ラムダ式でStrategyパターンで実装されたコードをシンプルにする ~ そこから見えてくるストラテジオブジェクトの本質

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

ログ出力(Strategyパターンバージョン)

 それではここから、簡易的なログ出力フレームワークを題材として、Strategyパターンを用いて作られたプログラムが、ラムダ式を使ってどのように書き直せるかを見ていきます。このログ出力フレームワークは、ログメッセージに現在時刻とロガー名を付加して、標準エラー出力(System.err)に書き出します。現在時刻の出力書式は、ログ出力フレームワークを使う側のプログラムが指定できます。

 まずは、Strategyパターンを使って実装した場合のプログラムを見てみましょう。このプログラムのクラス図は図4のとおりです。

図4:ログ出力プログラムのクラス図(Strategyパターン)
図4:ログ出力プログラムのクラス図(Strategyパターン)

 

 それぞれのクラス、インタフェースの役割は次のとおりです。

  • TimestampFormatter:時刻を文字列に変換するストラテジオブジェクトのインタフェース。Strategyの役割を担う。
  • EpochFormatter:時刻を、UTCの1970年1月1日0時ちょうどからの秒数(ミリ秒精度)を表す文字列に変換するストラテジオブジェクトのクラス。ConcreteStrategyの役割を担う。
  • LocalDateTimeFormatter:時刻を、日本時間の「yyyy-MM-dd'T'HH:mm:ss.SSS」形式に変換するストラテジオブジェクトのクラス。ConcreteStrategyの役割を担う。
  • Logger:ログを出力するクラス。Contextの役割を担う。
  • StrategyLoggerClientLogger クラスのインスタンスに TimestampFormatter インタフェースのストラテジオブジェクトを登録するクラス。クライアントの役割を担う。

 また、Strategyパターンを用いたプログラムのシーケンス図は図5のとおりです(一部のクラスは省略されています)。

図5:ログ出力プログラムのシーケンス図(Strategyパターン)
図5:ログ出力プログラムのシーケンス図(Strategyパターン)

 

 StrategyLoggerClient クラスは、Logger クラスのコンストラクタに TimestampFormatter インタフェースのストラテジオブジェクトを渡して、Logger クラスのインスタンスを生成します。その後、Logger クラスの log メソッドを用いてログを標準エラー出力に書き出します。

 Strategyパターンを用いたプログラムをリスト2に掲げます。

リスト2:StrategyLoggerClient.java ~ Strategyパターンを用いたログ出力プログラム
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

/** ロガー. */
class Logger {

    /** ロガーの名前. */
    private final String name;

    /** 時刻を文字列化するストラテジ. */
    private final TimestampFormatter formatter;

    Logger(String name, TimestampFormatter formatter) {
        this.name = name;
        this.formatter = formatter;
    }

    /** ログを出力する. */
    public void log(String message) {
        System.err.printf("%s [%s] %s%n", now(), this.name, message);
    }

    /** 現在時刻の文字列表現. */
    private String now() {
        return this.formatter.format(Instant.now());
    }

    /** 時刻を文字列化するストラテジのインタフェース. */
    interface TimestampFormatter {
        String format(Instant instant);
    }

}

public class StrategyLoggerClient {

    /** 時刻をエポック時間で文字列化するストラテジ. */
    static class EpochFormatter implements Logger.TimestampFormatter {
        @Override
        public String format(Instant instant) {
            BigDecimal milli = BigDecimal.valueOf(instant.toEpochMilli());
            BigDecimal sec = milli.divide(BigDecimal.valueOf(1000)).setScale(3);
            return sec.toPlainString();
        }
    }

    /** 時刻を「yyyy-MM-dd'T'HH:mm:ss.SSS」形式で文字列化するストラテジ. */
    static class LocalDateTimeFormatter implements Logger.TimestampFormatter {

        /** タイムゾーン. */
        private final ZoneId zoneId;

        LocalDateTimeFormatter(ZoneId zoneId) {
            this.zoneId = zoneId;
        }

        @Override
        public String format(Instant instant) {
            return LocalDateTime.ofInstant(instant, this.zoneId).toString();
        }
    }

    public static void main(String[] args) {
        // 時刻をエポック時間で表示するロガー
        Logger epochLogger = new Logger("epoch", new EpochFormatter());
        epochLogger.log("Hey!");
        epochLogger.log("Ho!");
        epochLogger.log("Let's go!");

        // 日本時間のタイムゾーン
        ZoneId jst = ZoneId.of("Asia/Tokyo");
        // 時刻を日本時間で表示するロガー
        Logger jstLogger = new Logger("jst", new LocalDateTimeFormatter(jst));
        jstLogger.log("Hey!");
        jstLogger.log("Ho!");
        jstLogger.log("Let's go!");
    }
}

 実行結果は次のとおりです。

1420438330.570 [epoch] Hey!
1420438330.635 [epoch] Ho!
1420438330.636 [epoch] Let's go!
2015-01-05T15:12:10.674 [jst] Hey!
2015-01-05T15:12:10.677 [jst] Ho!
2015-01-05T15:12:10.684 [jst] Let's go!

 Logger クラスの内部に時刻の文字列化を記述することなく、具体的な計算は登録されたストラテジオブジェクトに託しています。

次のページ
ログ出力(ラムダ式バージョン)

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

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

もっと読む

この記事の著者

宮川 拓(ミヤカワ タク)

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8401 2015/01/20 14:00

イベント

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

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

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

メールバックナンバー