3

Chain of Responsibility について読んだとき、クライアントを実際のデータ処理から分離することについて述べています。つまり、クライアント プログラムがチェーンの最初のメンバーを呼び出し、そのメンバーが要求を処理できるかどうかを判断し、処理できる場合は処理し、そうでない場合はチェーンの次のメンバーを呼び出すということです。

追加のメンバーをチェーンに追加でき、それを処理するためにクライアント プログラムを実際に変更する必要がないことを読みました。ウィキペディアを含む例で私が見ているのは、クライアントがチェーンの各メンバーをインスタンス化し、チェーンの各メンバーのサクセサーを設定する必要さえあるということです。

チェーン内のオブジェクトの順序がクライアントによって決定され、クライアントがチェーン内の各オブジェクトをインスタンス化する必要がある場合、これはどのように疎結合と見なされるのでしょうか?

4

1 に答える 1

2

責任の連鎖は、ケース ステートメントよりもはるかに柔軟です。重要なことに、CoR は次のことができます。

ハンドラーの関係と優先順位、または要求からハンドラーへのマッピングを配線することなく、要求を処理します。

つまり、クライアントは後続のハンドラーを認識せず、チェーンの存在さえも認識しません。

ハンドラー オブジェクトの数と種類はアプリオリにはわかりませんが、動的に構成できます。

つまり、実行時に新しいハンドラーを追加でき、既存のハンドラーを並べ替えることができます。

より基本的な答えは、case ステートメントは手続き型の構造であるため、Gang of Four 設計パターンなどのオブジェクト指向プログラミングでは一般的に使用されないというものです。

オンラインの例では、簡単にするためにクライアント内で CoR を構成する傾向があります。しかし実際には、それはパターンの目的に反するため、CoR は別の場所に設定されます。おもちゃの例は、チェーンがどのように見えるか、インスタンス化後にどのように動作するかを示すことを目的としています。しかしそれがインスタンス化される場所は、CoR を選択する動機の鍵となります。


例: クライアントはサービスに依存して String 値を処理します。

サービス API は自明です。

interface StringHandler {
    void handle(String arg);
}

クライアントは無限に複雑かもしれませんが、ある時点でサービスを呼び出します。

class Client {
    private final StringHandler argHandler;

    Client(StringHandler argHandler) {
        this.argHandler = argHandler;
    }

    void method(String arg) {
        argHandler.handle(arg);
        // more business logic...
    }
}

当社は、一連の責任としてサービスを実装することを選択します。

class ChainedHandler implements StringHandler {
    private final String handledString;
    private ChainedHandler next;

    ChainedHandler(String handledString) {
        this.handledString = handledString;
    }

    Optional<ChainedHandler> next() {
        return Optional.ofNullable(next);
    }

    ChainedHandler next(ChainedHandler handler) {
        ChainedHandler subsequent = next;
        next = handler;
        if (handler != null && subsequent != null)
            handler.next(subsequent);
        return this;
    }

    @Override
    public void handle(String arg) {
        if (arg.equalsIgnoreCase(handledString)) {
            System.out.println("Handled: " + arg);
        } else {
            next().ifPresentOrElse(
                    handler -> handler.handle(arg),
                    () -> System.out.println("No handler for: " + arg));
        }
    }
}

そのため、チェーンを構築し、それをクライアントに接続し、チェーンを変更していくつかのシナリオを実行します。

public static void main(String... commandLineArgs) {
    List<String> args = commandLineArgs.length > 0
            ? Arrays.asList(commandLineArgs)
            : List.of("foo", "bar", "baz", "qux");

    ChainedHandler chain = new ChainedHandler("foo")
            .next(new ChainedHandler("bar")
            .next(new ChainedHandler("baz")));

    Client client = new Client(chain);
    args.forEach(client::method);
    System.out.println();
    
    chain.next(new ChainedHandler("qux"));
    args.forEach(client::method);
    System.out.println();
    
    chain.next(null);
    args.forEach(client::method);
}

Clientはチェーンの存在を認識していないことに注意してください。さらに、コードを編集せずにチェーンを変更することに注意してください。これは、GoF が参照するデカップリングです。case ステートメントまたは if/else ブロックは、同じ柔軟性を提供しません。

于 2015-12-10T22:05:11.667 に答える