Цепочка ответственности VS Заявления о делах

Когда я читаю о цепочке ответственности, там говорится об отделении клиента от фактической обработки данных. Таким образом, он говорит, что клиентская программа вызывает первого члена цепочки, и этот член определяет, может ли он обработать запрос, и если может, он обрабатывает его, а если нет, то вызывает следующего члена цепочки и так далее.

Я читал, что в цепочку могут быть добавлены дополнительные члены, и клиентская программа не должна меняться, чтобы справиться с этим. Что я вижу в примерах, включая Википедию, так это то, что клиент должен создать экземпляр каждого члена цепочки и даже установить преемника для каждого члена цепочки.

Как это считается слабой связью, когда порядок объектов в цепочке определяется клиентом, и клиент даже должен создавать экземпляры каждого объекта в цепочке?


person user3393354    schedule 10.12.2015    source источник
comment
Если бы вы могли предоставить примеры кода, в которых используется оператор case, который, по вашему мнению, эквивалентен цепочке ответственности, было бы легче ответить.   -  person plalx    schedule 10.12.2015
comment
switch(planet) { case Mercury: PlanetHandler MercuryHandler = new MercuryHandler(); ломать; case Venus: PlanetHandler venusHandler = new VenusHandler();; ломать; // так далее... }   -  person user3393354    schedule 10.12.2015


Ответы (1)


Цепочка ответственности гораздо более гибкая, чем описание случая. Важно отметить, что CoR может:

обрабатывать запросы без жесткой связи обработчика и приоритета или сопоставления запроса с обработчиком.

Это означает, что клиенты не знают о каких-либо последующих обработчиках или даже о существовании цепочки.

Количество и тип объектов-обработчиков заранее неизвестны, их можно настроить динамически.

Это означает, что новые обработчики могут быть добавлены во время выполнения, а существующие обработчики могут быть переупорядочены.

Более простой ответ заключается в том, что операторы case представляют собой процедурную конструкцию и поэтому обычно не используются в объектно-ориентированное программирование, такое как шаблоны проектирования Gang of Four.

Онлайн-примеры могут иметь тенденцию к настройке CoR в клиенте для простоты; но на практике это противоречит цели шаблона, поэтому CoR будет настроен в другом месте. Примеры игрушек просто предназначены для того, чтобы показать, как выглядит цепочка и как она работает после инстанцирования; но где он создается, является ключом к мотивации выбора CoR.


Пример: клиент зависит от службы для обработки строкового значения.

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 не обеспечивают такой же гибкости.

person jaco0646    schedule 10.12.2015
comment
Как я уже сказал, пример, который я использовал, был очень простым, и порядок цепочки был установлен в реальной клиентской программе. Не было возможности установить новые обработчики без фактического изменения клиентской программы. - person user3393354; 11.12.2015
comment
Похоже, пример, который вы рассмотрели, на самом деле не реализует шаблон цепочки ответственности GoF. - person jaco0646; 11.12.2015
comment
Я посмотрел на многих, и все они делают это одинаково. Вот пример joezimjs.com/javascript/ - person user3393354; 12.12.2015
comment
И даже Википедия делает это следующим образом: ответственность_шаблон - person user3393354; 12.12.2015
comment
@ jaco0646 jaco0646 ... то, где он создается, является ключом к мотивации выбора CoR, это действительно хороший указатель для большинства из нас, не могли бы вы привести пример или ссылку на реальное использование? - person Taha Yavuz Bodur; 02.07.2020
comment
@TahaYavuzBodur, добавлен пример. - person jaco0646; 03.07.2020