ログ出力(Strategyパターンバージョン)
それではここから、簡易的なログ出力フレームワークを題材として、Strategyパターンを用いて作られたプログラムが、ラムダ式を使ってどのように書き直せるかを見ていきます。このログ出力フレームワークは、ログメッセージに現在時刻とロガー名を付加して、標準エラー出力(System.err)に書き出します。現在時刻の出力書式は、ログ出力フレームワークを使う側のプログラムが指定できます。
まずは、Strategyパターンを使って実装した場合のプログラムを見てみましょう。このプログラムのクラス図は図4のとおりです。
それぞれのクラス、インタフェースの役割は次のとおりです。
-
TimestampFormatter:時刻を文字列に変換するストラテジオブジェクトのインタフェース。Strategyの役割を担う。 -
EpochFormatter:時刻を、UTCの1970年1月1日0時ちょうどからの秒数(ミリ秒精度)を表す文字列に変換するストラテジオブジェクトのクラス。ConcreteStrategyの役割を担う。 -
LocalDateTimeFormatter:時刻を、日本時間の「yyyy-MM-dd'T'HH:mm:ss.SSS」形式に変換するストラテジオブジェクトのクラス。ConcreteStrategyの役割を担う。 -
Logger:ログを出力するクラス。Contextの役割を担う。 -
StrategyLoggerClient:LoggerクラスのインスタンスにTimestampFormatterインタフェースのストラテジオブジェクトを登録するクラス。クライアントの役割を担う。
また、Strategyパターンを用いたプログラムのシーケンス図は図5のとおりです(一部のクラスは省略されています)。
StrategyLoggerClient クラスは、Logger クラスのコンストラクタに TimestampFormatter インタフェースのストラテジオブジェクトを渡して、Logger クラスのインスタンスを生成します。その後、Logger クラスの log メソッドを用いてログを標準エラー出力に書き出します。
Strategyパターンを用いたプログラムをリスト2に掲げます。
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 クラスの内部に時刻の文字列化を記述することなく、具体的な計算は登録されたストラテジオブジェクトに託しています。


