デザインパターンを覚えたい (Chain of Responsibility)

Chain of Responsibilityパターン

処理をチェインさせて順次実行していき、目的の処理を決めるパターン。ポイントは、

  • 処理と要求を結びつきを弱めることができる
  • 状況に応じた処理の切り替えがしやすくなる

といったところですかね。

実装します。Javaの勉強したいので今回もJavaです。Loggerもどき作ります。
http://dl.dropbox.com/u/7810000/code/design_pattern/chain_of_responsibility.zip

実行結果

$ ant test
Buildfile: /Users/xxx/project/design_pattern/chain_of_responsibility/build.xml

build:
[javac] Compiling 1 source file to /Users/xxx/project/design_pattern/chain_of_responsibility/classes

test:
[java] LogLevelFilterHandler: ACCEPT logLevel[FATAL] message
[java] LogDumpHandler: DUMP logLevel[FATAL] msg[this is FATAL message]
[java] LogLevelFilterHandler: ACCEPT logLevel[ERROR] message
[java] LogDumpHandler: DUMP logLevel[ERROR] msg[this is ERROR message]
[java] LogLevelFilterHandler: ACCEPT logLevel[WARN] message
[java] LogDumpHandler: DUMP logLevel[WARN] msg[this is WARN message]
[java] LogLevelFilterHandler: DENY logLevel[INFO] message
[java] LogLevelFilterHandler: DENY logLevel[DEBUG] message
[echo] --- output dumped message ---
[exec] FATAL : this is FATAL message
[exec] ERROR : this is ERROR message
[exec] WARN : this is WARN message

BUILD SUCCESSFUL
Total time: 1 second

実装ではChain of Responsibilityパターンをロギング処理にて

  1. 対象ログレベルのチェック
  2. 実際のログのダンプ

と2つの処理をチェインさせる部分で使ってます。
# もしかして本来のパターンの使い方と違うかも

コードのポイントはハンドラ内の連続実行の部分かな。

public abstract class LogHandler {
    private LogHandler next;

    public LogHandler setNext(LogHandler next) {
        this.next = next;
        return next;
    }

    public final void logDump(int level, String msg) {
        if (!execute(level, msg) && next != null) {
            next.logDump(level, msg);
        }
    }

    protected abstract boolean execute(int level, String msg);
}

上のlogDumpメソッドの中でメンバ変数として持っている次のハンドラのメソッドをある意味再帰的に呼び出している。
ちなみにチェインさせている部分はLoggerクラスの中にあります。

public class Logger {
    private static Logger instance = null;
    private LogLevelFilterHandler logLevelFilterHandler;
    private LogDumpHandler logDumpHandler;

    private Logger(String propFile) throws Exception {
        Properties prop = new Properties();
        prop.load(new FileInputStream(propFile));

        int logLevel = LoggerUtils.getLogLevel(prop.getProperty("logLevel"));
        String logFile = prop.getProperty("logFile");

        logLevelFilterHandler = new LogLevelFilterHandler(logLevel);
        logDumpHandler = new LogDumpHandler(logFile);

        // Handlerをチェイン
        logLevelFilterHandler.setNext(logDumpHandler);
    }

    public static Logger getInstance() throws NullPointerException {
        if (instance == null) {
            System.out.println("ERROR: Logger is not setup");
            throw new NullPointerException();
        }
        return instance;
    }

    public static void setup(String propFile) throws Exception {
        instance = new Logger(propFile);
    }

    public void fatal(String msg) {
        logLevelFilterHandler.logDump(LogLevel.LOG_FATAL, msg);
    }

    /* --- 以下省略 --- */
}

初段のハンドラのメソッドをキックすることでチェインされたハンドラが必要に応じて実行される。