Зачем мне использовать цепочку ответственности вместо декоратора?
Я просто читаю шаблон цепочки ответственности, и мне трудно представить сценарий, когда я предпочел бы использовать его по сравнению с декоратором.
Как вы думаете? У CoR есть ниша использования?
11 ответов
Тот факт, что вы можете разорвать цепь в любой точке, отличает шаблон цепочки ответственности от шаблона декоратора. О декораторах можно думать как о выполнении всех сразу, без какого-либо взаимодействия с другими декораторами. Ссылки в цепочке могут рассматриваться как выполняющиеся по одной за раз, поскольку каждая из них зависит от предыдущей ссылки.
Используйте шаблон Chain of Responsibility, когда вы можете представить свою программу как цепочку, состоящую из ссылок, где каждая ссылка может обрабатывать запрос или передавать его по цепочке.
Когда я работал с Win32 API, мне иногда приходилось использовать предоставляемые им функции перехвата. Перехват сообщения Windows примерно соответствует шаблону цепочки ответственности. Когда вы перехватили сообщение типа WM_MOUSEMOVE, будет вызвана ваша функция обратного вызова. Думайте о функции обратного вызова как о последней ссылке в цепочке. Каждая ссылка в цепочке может решить, выбросить ли сообщение WM_MOUSEMOVE или передать его по цепочке к следующей ссылке.
Если бы в этом примере использовался шаблон Decorator, вы были бы уведомлены о сообщении WM_MOUSEMOVE, но вы были бы бессильны помешать другим обработчикам также обработать его.
Другое место, где используется шаблон "Цепочка командования", - игровые движки. Опять же, вы можете подключить функции двигателя, события и другие вещи. В случае игрового движка вы не хотите просто добавлять функциональность. Вы хотите добавить функциональность и не дать игровому движку выполнить действие по умолчанию.
Разница между этими шаблонами не связана с тем, когда или как цепочка может быть разорвана (что предполагает цепочку) или когда выполняется дополнительное поведение. Они связаны тем, что оба используют композицию в пользу наследования, чтобы обеспечить более гибкое решение.
Ключевое отличие состоит в том, что декоратор добавляет новое поведение, которое фактически расширяет оригинальный интерфейс. Это похоже на то, как обычное расширение может добавлять методы, за исключением того, что "подкласс" связан только со ссылкой, что означает, что можно использовать любой "суперкласс".
Шаблон COR может изменить существующее поведение, которое аналогично переопределению существующего метода с использованием наследования. Вы можете вызвать super.xxx(), чтобы продолжить цепочку или обработать сообщение самостоятельно.
Таким образом, разница невелика, но пример декоратора должен помочь:
interface Animal
{
Poo eat(Food food);
}
class WalkingAnimal implements Animal
{
Animal wrapped;
WalkingAnimal(Animal wrapped)
{
this.wrapped = wrapped;
}
Position walk(Human walker)
{
};
Poo eat(Food food)
{
return wrapped.eat(food);
}
}
class BarkingAnimal implements Animal
{
Animal wrapped;
BarkingAnimal(Animal wrapped)
{
this.wrapped = wrapped;
}
Noise bark()
{
};
Poo eat(Food food)
{
bark();
return wrapped.eat();
}
}
Вы можете видеть, что мы можем составить шагающего, лающего животного... или фактически добавить способность лаять любому животному. Чтобы использовать это дополнительное поведение напрямую, нам нужно сохранить ссылку на декоратор BarkingAnimal.
Все BarkingAnimal также лают один раз перед едой, что изменило существующую функциональность и поэтому похоже на COR. Но цель не то же самое, что COR, то есть найти одно животное из многих, которое будет есть пищу. Намерение здесь состоит в том, чтобы изменить поведение.
Вы могли бы представить, что COR применяется для поиска человека, который возьмет животное на прогулку. Это может быть реализовано в виде связанного списка, как chained
выше или как явный список... или как угодно.
Надеюсь, это достаточно ясно!
Джон
Я бы сказал, что цепь ответственности - это особая форма декоратора.
цепь
Старайтесь не связывать отправителя запроса с получателем, предоставляя более чем одному объекту возможность обработать запрос. Цепочка получения объектов и передачи запроса по цепочке, пока объект не обрабатывает его.
против
декоратор
Прикрепите дополнительные обязанности к объекту динамически. Декораторы предоставляют гибкую альтернативу подклассам для расширения функциональности.
Я бы сказал, что порядок будет происходить. Если вы приковали их цепочкой, то они будут вызваны по всей цепочке. С декоратором вам не гарантирован этот заказ, только дополнительные обязанности могут быть наложены.
Декоратор используется, когда вы хотите добавить функциональность к объекту.
COR используется, когда один из многих действующих лиц может действовать на объекте.
Определенный Декоратор вызывается, чтобы предпринять действие, основанное на типе; в то время как COR передает объект по определенной цепочке, пока один из действующих лиц не решит, что действие завершено.
COR может использоваться, когда существует несколько уровней эскалации к разным обработчикам - например, в колл-центр, где значение клиента для компании определяет, переходит ли вызов на определенный уровень поддержки.
Ну, я могу вспомнить 2 ситуации:
- У вас нет основного объекта, т.е. вы не знаете, что делать с запросом после того, как он прошел все слои / фильтры. (что-то вроде цепочки перехватчиков, которой не важно, где заканчивается запрос).
- Вам необходимо выборочно применить некоторую предварительную или последующую обработку к запросу. Не в общей форме улучшения, как декоратор. то есть фильтры могут обрабатывать или не обрабатывать определенный запрос, но добавление декоратора всегда расширяет ваш объект с помощью некоторых функций.
Не могу больше думать прямо сейчас, хотел бы услышать больше в этой теме.
Я согласен, что со структурной точки зрения эти две модели очень похожи. Моя мысль о конечном поведении:
В классической интерпретации элемента CoR, который обрабатывает запрос, разрывается цепочка.
Если какой-либо элемент в декораторе разрывает цепочку, то реализация декоратора будет неправильной, потому что базовая часть поведения будет потеряна. И идея декоратора - прозрачное добавление нового поведения, когда базовое поведение остается нетронутым.
декоратор
Шаблон декоратора позволяет динамически добавлять поведение к отдельному объекту.
Он предоставляет гибкую альтернативу подклассам для расширения функциональности. Несмотря на то, что он использует наследование, он наследуется от интерфейса Lowest Common Denominator ( LCD).
UML-диаграмма для декоратора
Последствия:
- С отделкой также можно динамически удалять добавленные функции.
- Декорирование добавляет функциональность объектам во время выполнения, что усложняет отладку функциональности системы.
Полезные ссылки:
Когда использовать шаблон декоратора?
Decorator_pattern из Википедии
декоратор от создания источников
Цепочка ответственности:
Шаблон цепочки ответственности - это шаблон проектирования, состоящий из источника объектов команд и серии объектов обработки. Каждый объект обработки содержит логику, которая определяет типы объектов команд, которые он может обрабатывать; остальные передаются следующему объекту обработки в цепочке
Диаграмма UML
Этот шаблон более эффективен, когда:
- Больше чем один объект может обработать команду
- Обработчик заранее не известен
- Обработчик должен определяться автоматически
- Желательно, чтобы запрос был адресован группе объектов без явного указания его получателя.
- Группа объектов, которые могут обрабатывать команду, должна указываться динамически
Полезные ссылки:
Chain-of-ответственность_pattern из Википедии
шаблон цепи ответственности от oodesign
chain_of_responsibility от создания источника
Пример из реальной жизни: в компании назначенная роль имеет определенные ограничения для обработки запроса на покупку. Если у человека с указанной ролью недостаточно полномочий для утверждения счета на покупку, он направит команду / запрос своему преемнику, у которого будет больше полномочий. Эта цепочка будет продолжаться до тех пор, пока команда не будет обработана.
Прочитав определения "Банды четырех", я не уверен, что есть реальная разница. (включено для удобства)
- Декоратор: позволяет динамически переносить объекты с целью изменения их существующих обязанностей и поведения.
- Chain of Responsibility: дает более чем одному объекту возможность обрабатывать запрос, связывая получаемые объекты вместе
Википедия немного раскрывает их, но некоторые из них произвольны.
- Декоратор обычно реализуется в виде связанного списка. Но я думаю, что это слишком низкий уровень, чтобы считаться "частью" паттерна.
- Ссылки Chain of Responsibility обрабатывают данные только в том случае, если это их ответственность; но определение ответственности и обработка данных являются частью поведения. Декораторы могут сделать это так же легко.
- Декоратор требует, чтобы вы вызвали делегата.
- "Чистая" ссылка CoR должна вызывать делегат, только если он не обрабатывает данные.
Первые два атрибута на самом деле не различают шаблоны. Вторые два делают, но способ, которым обычно реализуются Decorator и CoR, не применяет эти атрибуты - дизайнер надеется, что никто не напишет Decorator, который разрывает цепочку, или CoRLink, который продолжает цепочку после обработки данных.
Чтобы на самом деле реализовать эти атрибуты, вам нужно что-то вроде следующего.
Принудительный декоратор:
abstract class Decorated {
public Decorated delegate;
public final Object doIt(Object args) {
Object returnVal = behavior(arg);
if(delegate != null) returnVal = delegate.doit(returnVal);
return returnVal;
}
protected abstract Object behavior(Object args); //base or subclass behavior
}
Принудительная цепочка ответственности:
abstract class Link {
public Link delegate;
public final Object processIt(Obect args) {
Object returnVal = args;
if(isMyResponsibility) returnVal = processingBehavior(returnVal);
else returnVal = delegate.processIt(returnVal);
return returnVal;
}
protected abstract Boolean isMyResponsibility(Object args);
protected abstract Object processingBehavior(Object args);
}
(В качестве альтернативы, вы можете просто добавить строку в Javadoc, если все, что вам нужно, это освободить себя от ответственности в случае, если кто-то еще испортит ваш дизайн - но зачем оставлять это на волю случая?)
- ключевое слово "extends" - статическое расширение.
- Шаблон декоратора - динамическое расширение.
- Шаблон Chain Of Responsibility - просто обработка объекта команды с набором объектов обработки, и эти объекты не знают друг друга.
Я думаю, что ситуации для применения этих двух моделей разные. И, кстати, для шаблона декоратора, декоратор должен знать компонент, который он обернул. А для CoR разные перехватчики не могли ничего знать друг о друге.