Javaにおける責任のデザインパターンの連鎖

Javaの責任のチェーンデザインパターン

1. 前書き

この記事では、広く使用されているbehavioral design patternChain of Responsibilityについて見ていきます。

previous articleでより多くのデザインパターンを見つけることができます。

2. 責任の連鎖

Wikipediaは、「コマンドオブジェクトのソースと一連の処理オブジェクト」からなる設計パターンとして責任感のチェーンを定義している。

チェーン内の各処理オブジェクトは、特定のタイプのコマンドを処理し、処理が行われた後、コマンドをチェーン内の次のプロセッサに転送します。

Chain of Responsibilityパターンは次の場合に便利です。

  • コマンドの送信者と受信者を分離する

  • 処理時に処理戦略を選択する

それでは、パターンの簡単な例を見てみましょう。

3. 例

Chain of Responsibilityを使用して、認証リクエストを処理するためのチェーンを作成します。

したがって、入力認証プロバイダーはcommandになり、各認証プロセッサーは個別のprocessorオブジェクトになります。

まず、プロセッサの抽象基本クラスを作成しましょう。

public abstract class AuthenticationProcessor {

    public AuthenticationProcessor nextProcessor;

    // standard constructors

    public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}

次に、AuthenticationProcessorを拡張する具象プロセッサを作成しましょう。

public class OAuthProcessor extends AuthenticationProcessor {

    public OAuthProcessor(AuthenticationProcessor nextProcessor) {
        super(nextProcessor);
    }

    @Override
    public boolean isAuthorized(AuthenticationProvider authProvider) {
        if (authProvider instanceof OAuthTokenProvider) {
            return true;
        } else if (nextProcessor != null) {
            return nextProcessor.isAuthorized(authProvider);
        }

        return false;
    }
}
public class UsernamePasswordProcessor extends AuthenticationProcessor {

    public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) {
        super(nextProcessor);
    }

    @Override
    public boolean isAuthorized(AuthenticationProvider authProvider) {
        if (authProvider instanceof UsernamePasswordProvider) {
            return true;
        } else if (nextProcessor != null) {
            return nextProcessor.isAuthorized(authProvider);
        }
        return false;
    }
}

ここでは、着信承認リクエスト用に2つの具体的なプロセッサを作成しました:UsernamePasswordProcessorOAuthProcessor.

それぞれについて、isAuthorizedメソッドをオーバーライドします。

それでは、いくつかのテストを作成しましょう。

public class ChainOfResponsibilityTest {

    private static AuthenticationProcessor getChainOfAuthProcessor() {
        AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null);
        return new UsernamePasswordProcessor(oAuthProcessor);
    }

    @Test
    public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() {
        AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
        assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider()));
    }

    @Test
    public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() {
        AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();

        assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider()));
    }
}

上記の例では、認証プロセッサのチェーンを作成します:UsernamePasswordProcessor → OAuthProcessor。 最初のテストでは承認が成功し、もう1つのテストでは失敗します。

まず、UsernamePasswordProcessorは、認証プロバイダーがUsernamePasswordProviderのインスタンスであるかどうかを確認します。

期待される入力ではないため、UsernamePasswordProcessorOAuthProcessorに委任します。

最後に、OAuthProcessorがコマンドを処理します。 最初のテストでは、一致があり、テストに合格します。 2番目の方法では、チェーンにプロセッサがもうないため、テストが失敗します。

4. 実装の原則

Chain of Responsibilityを実装する際には、いくつかの重要な原則を念頭に置く必要があります。

  • チェーン内の各プロセッサには、コマンドを処理するための実装があります

    • 上記の例では、すべてのプロセッサにisAuthorizedの実装があります

  • チェーン内のすべてのプロセッサは、次のプロセッサを参照する必要があります

    • 上記では、UsernamePasswordProcessorOAuthProcessorに委任しています

  • 各プロセッサは次のプロセッサに委任する責任があるため、ドロップされたコマンドに注意してください

    • この例でも、コマンドがSamlProviderのインスタンスである場合、リクエストは処理されず、許可されない可能性があります

  • プロセッサは再帰的なサイクルを形成するべきではありません

    • この例では、チェーンにサイクルがありません:UsernamePasswordProcessor → OAuthProcessor.しかし、UsernamePasswordProcessorOAuthProcessor,の次のプロセッサとして明示的に設定した場合then we end up with a cycle in our chainUsernamePasswordProcessor → OAuthProcessor → UsernamePasswordProcessor.■コンストラクターで次のプロセッサーを使用すると、これに役立ちます。

  • チェーン内の1つのプロセッサのみが特定のコマンドを処理します

    • この例では、着信コマンドにOAuthTokenProviderのインスタンスが含まれている場合、OAuthProcessorのみがコマンドを処理します

5. 実世界での使用法

Javaの世界では、Chain of Responsibilityの恩恵を毎日受けています。 複数のフィルターがHTTP要求を処理できるようにするOne such classic example is Servlet Filters inJava。 その場合でも、each filter invokes the chain instead of the next filter.

サーブレットフィルタ:でこのパターンをよりよく理解するために、以下のコードスニペットを見てみましょう。

public class CustomFilter implements Filter {

    public void doFilter(
      ServletRequest request,
      ServletResponse response,
      FilterChain chain)
      throws IOException, ServletException {

        // process the request

        // pass the request (i.e. the command) along the filter chain
        chain.doFilter(request, response);
    }
}

上記のコードスニペットに示されているように、チェーン内の次のプロセッサにリクエストを渡すには、FilterChaindoFilterメソッドを呼び出す必要があります。

6. デメリット

そして、Chain of Responsibilityがいかに興味深いかを見てきたので、いくつかの欠点を覚えておきましょう。

  • ほとんどの場合、簡単に破損する可能性があります。

    • プロセッサが次のプロセッサの呼び出しに失敗すると、コマンドはドロップされます

    • プロセッサが間違ったプロセッサを呼び出すと、サイクルにつながる可能性があります

  • 深いスタックトレースを作成し、パフォーマンスに影響を与える可能性があります。

  • プロセッサ間でコードが重複する可能性があり、メンテナンスが増加します

7. 結論

この記事では、受信認証要求を承認するチェーンを使用して、Chain of Responsibilityとその長所と短所について説明しました。

そして、いつものように、ソースコードはover on GitHubで見つけることができます。