JavaのChainofResponsibilityDesignPattern
1. 序章
この記事では、広く使用されている動作設計パターン: Chain ofResponsibilityを見ていきます。
前の記事でより多くのデザインパターンを見つけることができます。
2. 責任の連鎖
Wikipedia は、Chain of Responsibilityを、「コマンドオブジェクトのソースと一連の処理オブジェクト」で構成されるデザインパターンとして定義しています。
チェーン内の各処理オブジェクトは、特定のタイプのコマンドを担当し、処理が実行されると、コマンドがチェーン内の次のプロセッサに転送されます。
Chain of Responsibilityパターンは、次の場合に便利です。
- コマンドの送信者と受信者を分離する
- 処理時に処理戦略を選択する
それでは、パターンの簡単な例を見てみましょう。
3. 例
Chain of Responsibilityを使用して、認証要求を処理するためのチェーンを作成します。
したがって、入力認証プロバイダーはコマンドになり、各認証プロセッサーは個別のプロセッサーオブジェクトになります。
まず、プロセッサの抽象基本クラスを作成しましょう。
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()));
}
}
上記の例では、認証プロセッサのチェーンを作成します: UsersnamePasswordProcessor->OAuthProcessor。 最初のテストでは承認は成功し、他のテストでは失敗します。
まず、 UsersnamePasswordProcessor は、認証プロバイダーがUsersnamePasswordProviderのインスタンスであるかどうかを確認します。
予期された入力ではないため、UsersnamePasswordProcessorはOAuthProcessorに委任します。
最後に、OAuthProcessorがコマンドを処理します。 最初のテストでは、一致があり、テストに合格します。 2番目の例では、チェーン内にこれ以上プロセッサがないため、テストは失敗します。
4. 実装原則
Chain of Responsibilityを実装する際には、いくつかの重要な原則を念頭に置く必要があります。
- チェーン内の各プロセッサには、コマンドを処理するための実装があります
- 上記の例では、すべてのプロセッサにisAuthorizedの実装があります。
- チェーン内のすべてのプロセッサは、次のプロセッサを参照する必要があります
- 上記では、UsersnamePasswordProcessorはOAuthProcessorに委任します
- 各プロセッサは次のプロセッサに委任する責任があるため、ドロップされたコマンドに注意してください
- この例でも、コマンドが SamlProvider のインスタンスである場合、リクエストは処理されず、許可されない可能性があります
- プロセッサは再帰的なサイクルを形成するべきではありません
- この例では、チェーンにサイクルがありません。 UsernamePasswordProcessor-> OAuthProcessor 。 ただし、明示的に設定すると UsernamePasswordProcessor 次のプロセッサとして OAuthProcessor、 それから私達は私達のチェーンのサイクルで終わります :
UsernamePasswordProcessor->OAuthProcessor->UsernamePasswordProcessor。 コンストラクターで次のプロセッサーを使用すると、これに役立ちます
- この例では、チェーンにサイクルがありません。 UsernamePasswordProcessor-> OAuthProcessor 。 ただし、明示的に設定すると UsernamePasswordProcessor 次のプロセッサとして OAuthProcessor、 それから私達は私達のチェーンのサイクルで終わります :
- チェーン内の1つのプロセッサのみが特定のコマンドを処理します
- この例では、着信コマンドに OAuthTokenProvider のインスタンスが含まれている場合、OAuthProcessorのみがコマンドを処理します
5. 実世界での使用法
Javaの世界では、ChainofResponsibilityの恩恵を毎日受けています。 そのような典型的な例の1つは、 Java のサーブレットフィルターで、複数のフィルターがHTTPリクエストを処理できるようにします。 その場合でも、各フィルターは次のフィルターの代わりにチェーンを呼び出します。
サーブレットフィルタ:でこのパターンをよりよく理解するために、以下のコードスニペットを見てみましょう。
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とその長所と短所について、チェーンを使用して着信認証要求を承認する方法について説明しました。
そして、いつものように、ソースコードはGitHubのにあります。