Каковы преимущества цепочки ответственности перед списками классов?

Недавно я обсуждал с другим программистом лучший способ рефакторинга огромного (1000 строк) метода, полного & quot; if & quot; заявления.

Код написан на Java, но я думаю, что эта проблема может возникнуть и в других языках, таких как C #.

Чтобы решить эту проблему, он предложил использовать схему цепочки ответственности. Он предложил иметь базовый «обработчик» учебный класс. Затем «Обработчик1», «Обработчик2» и т. Д. Будет расширять «Обработчик».
Тогда обработчики будут иметь «getSuccessor» метод, который будет либо возвращать ноль (если он был последним в цепочке), либо следующий обработчик цепочки.
Затем & quot; handleRequest (Запрос) & quot; функция будет либо иметь дело с запросом, либо передать его следующей цепочке и, если ни одно из предыдущих решений не сработает, вернет просто ноль или сгенерирует исключение.
Чтобы добавить новый обработчик в цепочку, кодировщик должен перейти к последнему элементу цепочки и сообщить ему, что появился новый элемент. Чтобы что-то сделать, он просто вызывает handleRequest для первого элемента цепочки.

Чтобы решить эту проблему, я предложил использовать другой подход.
У меня "есть базовый" обработчик " также с «Handler1», «Handler2», как и в предыдущем методе.
Тем не менее, не будет «getSuccessor» метод. Вместо этого у меня есть класс Collection со списком обработчиков (Vector, ArrayList или любой другой, который лучше всего подходит в этом случае).
Функция handleRequest все еще существует, но она не будет передавать вызов следующим обработчикам. Это только обработало бы запрос или возвратило бы нуль.
Чтобы обработать запрос, можно использовать

<code>for(Handler handle : handlers){
    result = handle.handleRequest(request);
    if(result!=null) return result;
}
throw new CouldNotParseRequestException(); //just like in the other approach
</code>

Или, чтобы предотвратить дублирование кода, «parseRequest (request)» quot; метод может быть добавлен в класс коллекции. Чтобы добавить новый обработчик, нужно перейти в конструктор коллекции (или статический блок {}, или что-то эквивалентное) и просто добавить код «addHandler (new Handler3 ());».

Какие именно преимущества цепочки ответственности мне не хватает при таком подходе? Какой метод лучше (еслиis лучший метод)? Зачем? Какие потенциальные ошибки и проблемы может вызвать каждый метод проектирования?

Для тех, кому нужен контекст, вот как выглядел оригинальный код:

<code>if(x instanceof Type1)
{
//doSomething1
} else if(x instanceof Type2)
{
//doSomething2
}
//etc.
</code>

Ответы на вопрос(3)

является ли Chain of Responsibility вашим ответом или применимо ли GoF. Посетитель может быть правильным. Недостаточно информации, чтобы быть уверенным.

Вполне возможно, что с твоей проблемой можно справиться с помощью старомодного полиморфизма. Или, может быть, карта, которая использовала ключи, чтобы выбрать соответствующий объект-обработчик.

Держите в уме «делайте самые простые вещи». Не прыгайте в комплекс, пока не докажете себе, что он вам нужен.

Я недавно читал о Анти-IF кампания которая продвигает эту идею. Звучит вполне уместно здесь.

Решение Вопроса

чем эти преемники. Это позволяет легко и понятно манипулировать этим набором обработчиков: интерфейс коллекций хорошо известен, и все понимают, как выполнять итерацию по списку, а что нет.

Если вы используете этот преемник, предложенный другом, постарайтесь не впасть в очень глубокую рекурсию (если ваша платформа не поддерживает хвостовые вызовы, я не знаю, способны ли JVM на это).

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

 luiscubal28 июн. 2009 г., 20:22
Очень глубокая рекурсия не должна быть проблемой. Он относительно небольшой (далеко не тысячи вызовов методов, необходимых для переполнения системы

что хотят делать ваши обработчики.

Если обработчики могут полностью обработать запрос на запрос самостоятельно, ваш подход в порядке. Обработчики не имеют ссылки на другие обработчики, что делает интерфейс обработчика простым. В отличие от стандартной реализации Chain of Responsibility, вы можете добавлять или удалять обработчики из середины цепочки. На самом деле вы можете создавать различные цепочки в зависимости от типа запроса.

Одна из проблем вашего подхода заключается в том, что обработчик не может выполнить предварительную или последующую обработку запроса. Если эта функциональность необходима, тогда лучше использовать Chain of Responsibility. В CoR обработчик является ответственным за делегирование следующему обработчику в цепочке, поэтому обработчик может выполнять предварительную обработку и / или постобработку, включая изменение или замену ответа от следующего обработчика в цепочке. Таким образом, CoR очень похож на Decorator; это просто намерение, которое отличается.

Поскольку в CoR, обработчик сохраняет ссылку на следующий элемент в цепочке, вы не можете добавлять или удалять элементы из середины цепочки. Вариант CoR, который позволяет добавлять или удалять элементы из середины цепочки, представляет собой цепочку фильтров (см., Например, Javax.servlet.FilterChain).

Пример кода, который вы показали, представлял собой набор операторов «if», которые вели себя по-разному в зависимости от типа объекта. Если это типично для кода, который вы очищаете, вы можете просто получить карту от типа запроса до требуемого обработчика.

Другой подход к удалению операторов if - это наследование. Если у вас было какое-то поведение, которое вам нужно было сделать, и был один вариант для веб-сервера и другой вариант для сервера SOAP, вы могли бы иметь WebServerRequestHandler и SoapServerRequestHandler, каждый из которых расширяет RequestHandler. Преимущество наследования заключается в том, что существует более ясное место для размещения логики, которая является общей для обоих типов запросов. Недостатком является то, что, поскольку Java не имеет множественного наследования, вы можете моделировать только одномерные проблемы.

Ваш ответ на вопрос