2009-06-28 35 views
13

Недавно я обсуждал с другим программистом лучший способ рефакторинга огромного (1000 строк) метода, полного инструкций «если».В чем преимущества цепочки ответственности против списков классов?

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

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

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

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

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

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

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

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

ответ

6

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

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

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

+0

Очень глубокая рекурсия не должна быть проблемой. Он относительно невелик (нигде рядом с тысячами вызовов методов, необходимых для переполнения системы). – luiscubal

0

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

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

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

Недавно я прочитал об Anti-IF campaign, что способствует этой идее. Звучит вполне уместно.

4

Какой подход лучше всего зависит от того, что хотят сделать ваши обработчики.

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

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

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^