責任の連鎖パターン

責任の連鎖パターン(Chain of Responsibility Pattern)は、リクエストのために受信者オブジェクトのチェーンを作成します。このパターンは、リクエストの種類に応じて、リクエストの送信者と受信者を疎結合にします。このタイプのデザインパターンは、振る舞いパターンに属します。

責任の連鎖パターンは、複数のハンドラー(処理オブジェクト)をチェーン構造で接続し、リクエストがこのチェーンに沿って渡され、いずれかのハンドラーがそのリクエストを処理するまで伝播させます。

責任の連鎖パターンは、複数のオブジェクトにリクエストを処理する機会を与え、それによりリクエストの送信者と受信者の間の結合関係を回避します。これらのオブジェクトを1つのチェーンに連結し、そのチェーンに沿ってリクエストを渡します。

紹介

意図

リクエストが処理されるまで、リクエストをハンドラーのチェーンに沿って渡すことを許可します。

主に解決する問題

  • リクエストの送信者と受信者を疎結合にし、複数のオブジェクトがリクエストを受信できるようにします。送信者はどのオブジェクトがそれを処理するかを知る必要がありません。

使用シーン

  • 複数のオブジェクトがリクエストを処理でき、どのオブジェクトが処理するかが実行時に決まる場合。
  • 複数のオブジェクトのうちの1つにリクエストを提出する必要があるが、受信者を明示的に指定したくない場合。

実装方法

  • ハンドラーインターフェースを定義する:すべてのハンドラーは同じインターフェースを実装する必要があります。
  • 具体的なハンドラーを作成する:インターフェースを実装する具象クラスで、リクエスト処理ロジックとチェーン内の次のハンドラーへの参照を含みます。

重要なコード

  • Handlerインターフェース:リクエストを処理するためのメソッドを定義します。
  • ConcreteHandlerクラス:Handlerインターフェースを実装し、リクエスト処理ロジックと次のハンドラーへの参照を含みます。

応用例

  1. 太鼓を叩いて花を回すゲーム:音楽が止まるまで行われるゲーム内の受け渡し行為。
  2. イベントバブリング:JavaScriptでは、イベントは最も具体的な要素から始まり、段階的に上位へ伝播します。
  3. Webサーバー:Apache Tomcatの文字エンコーディング処理、Struts2のインターセプター、ServletのFilterなど。

長所

  1. 結合度の低減:送信者と受信者の間を疎結合にします。
  2. オブジェクトの簡素化:オブジェクトはチェーンの構造を知る必要がありません。
  3. 柔軟性:チェーンのメンバーや順序を変更することで、責任を動的に追加または削除できます。
  4. 拡張が容易:新しいリクエスト処理クラスの追加がとても簡単です。

短所

  1. リクエストが処理されない:リクエストが必ずチェーン内のいずれかのハンドラーに受信されるとは保証できません。
  2. パフォーマンスへの影響:システムのパフォーマンスに影響を与える可能性があり、デバッグが困難で、循環呼び出しを引き起こす可能性があります。
  3. 観察が困難:実行時の特徴が明確でなく、デバッグを妨げる可能性があります。

使用上のアドバイス

  • リクエストを処理する際、潜在的なハンドラーが複数ある場合は、責任の連鎖パターンの使用を検討してください。
  • チェーン内の各ハンドラーが、リクエストをチェーンの次の要素に渡す方法を明確に把握していることを確認してください。

注意事項

  • Java Web開発では、責任の連鎖パターンはフィルターチェーンやインターセプターなどで広く応用されています。

構造

主に以下のコアとなる役割が関わります。

  • 抽象ハンドラー(Handler):

    • リクエストを処理するインターフェースを定義し、通常はリクエストを処理するメソッド(例:handleRequest)と、次のハンドラーを指す参照(後続者)を含みます。
  • 具象ハンドラー(ConcreteHandler):

    • 抽象ハンドラーインターフェースを実装し、リクエストの処理を担当します。そのリクエストを処理できる場合は直接処理し、できない場合は次のハンドラーにリクエストを渡します。
  • クライアント(Client):

    • ハンドラーオブジェクトを作成し、それらを1つの責任のチェーンに接続します。通常、クライアントはリクエストを責任のチェーンの最初のハンドラーに送るだけでよく、リクエストの具体的な処理プロセスを気にする必要はありません。

実装

抽象クラスを作成しますAbstractLogger、詳細なログ記録レベルを持ちます。次に、この抽象クラスを拡張する3種類のロガーを作成します。AbstractLogger各ロガーは、メッセージのレベルが自分のレベルかどうかを確認し、該当する場合はそのレベルに応じて出力し、該当しない場合は出力せずに次のロガーにメッセージを渡します。

责任链模式的 UML 图

ステップ 1

抽象ロガークラスを作成します。

AbstractLogger.java

public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //責任の連鎖における次の要素 protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); }

ステップ 2

このロガークラスを拡張したエンティティクラスを作成します。

ConsoleLogger.java

public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } }

ErrorLogger.java

public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } }

FileLogger.java

public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }

ステップ 3

異なるタイプのロガーを作成します。それぞれに異なるエラーレベルを割り当て、各ロガーに次のロガーを設定します。各ロガー内の次のロガーは、チェーンの一部を表します。

ChainPatternDemo.java

public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information."); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information."); } }

ステップ 4

プログラムを実行して、結果を出力します:

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.
その他の拡張