Javaの責任のチェーンデザインパターン
1. 前書き
この記事では、広く使用されているbehavioral design pattern:Chain 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つの具体的なプロセッサを作成しました:UsernamePasswordProcessorとOAuthProcessor.
それぞれについて、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のインスタンスであるかどうかを確認します。
期待される入力ではないため、UsernamePasswordProcessorはOAuthProcessorに委任します。
最後に、OAuthProcessorがコマンドを処理します。 最初のテストでは、一致があり、テストに合格します。 2番目の方法では、チェーンにプロセッサがもうないため、テストが失敗します。
4. 実装の原則
Chain of Responsibilityを実装する際には、いくつかの重要な原則を念頭に置く必要があります。
-
チェーン内の各プロセッサには、コマンドを処理するための実装があります
-
上記の例では、すべてのプロセッサにisAuthorizedの実装があります
-
-
チェーン内のすべてのプロセッサは、次のプロセッサを参照する必要があります
-
上記では、UsernamePasswordProcessorはOAuthProcessorに委任しています
-
-
各プロセッサは次のプロセッサに委任する責任があるため、ドロップされたコマンドに注意してください
-
この例でも、コマンドがSamlProviderのインスタンスである場合、リクエストは処理されず、許可されない可能性があります
-
-
プロセッサは再帰的なサイクルを形成するべきではありません
-
この例では、チェーンにサイクルがありません:UsernamePasswordProcessor → OAuthProcessor.しかし、UsernamePasswordProcessorをOAuthProcessor,の次のプロセッサとして明示的に設定した場合then we end up with a cycle in our chain:UsernamePasswordProcessor → 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);
}
}
上記のコードスニペットに示されているように、チェーン内の次のプロセッサにリクエストを渡すには、FilterChainのdoFilterメソッドを呼び出す必要があります。
6. デメリット
そして、Chain of Responsibilityがいかに興味深いかを見てきたので、いくつかの欠点を覚えておきましょう。
-
ほとんどの場合、簡単に破損する可能性があります。
-
プロセッサが次のプロセッサの呼び出しに失敗すると、コマンドはドロップされます
-
プロセッサが間違ったプロセッサを呼び出すと、サイクルにつながる可能性があります
-
-
深いスタックトレースを作成し、パフォーマンスに影響を与える可能性があります。
-
プロセッサ間でコードが重複する可能性があり、メンテナンスが増加します
7. 結論
この記事では、受信認証要求を承認するチェーンを使用して、Chain of Responsibilityとその長所と短所について説明しました。
そして、いつものように、ソースコードはover on GitHubで見つけることができます。