Этот шаблон проектирования относится к категории поведенческих шаблонов проектирования. Основной целью цепочки ответственности является слабая связь, в качестве примера рассмотрим связь между отправителем и получателем, отправитель не знает, кто является получателем, а получатель не знает, кто является отправителем. Это просто означает, что этот шаблон проектирования разделяет отправителя и получателя запроса на основе типа запроса.
Ситуация, в которой разработчики могут использовать шаблон цепочки ответственности?
· Когда разработчик хочет разделить отправителя и получателя запроса
· Несколько объектов, определенных во время выполнения, являются кандидатами для обработки запроса
· Когда разработчик не хочет явно указывать обработчики в коде
· Когда разработчик хочет сделать запрос к одному из нескольких объектов без явного указания получателя.
Как вы можете видеть на приведенной выше диаграмме UML, в этом шаблоне проектирования он использует класс, называемый обработчиком, для разделения отправителя и получателя.
Пример:
Предположим, что авиакомпании требуется программное обеспечение, которое создает план количества топлива, который будет рассчитывать потребность самолета в топливе на основе его летных часов.
Шаг 1: Реализуйте абстрактный класс обработчика.
В классе обработчика создайте ссылочную переменную, которая содержит преемника, и создайте абстрактный метод с именем
«рассчитатьMaxFuelQuantity (FuelQuantityPlan fuelQuantityPlan)».
package ChainOfResponsibilityExample; public abstract class FuelQuantityHandler { protected FuelQuantityHandler successor; public void setSuccessor(FuelQuantityHandler successor) { this.successor = successor; } public abstract float calculateMaxFuelQuantity (FuelQuantityPlan fuelQuantityPlan); }
Примечание. Кроме того, этот класс можно создать как класс интерфейса, а не реализовать как абстрактный класс. Но в этом примере я реализовал его как абстрактный класс.
Шаг 2: Создайте класс плана количества топлива, объявите переменные экземпляра и реализуйте соответствующие конструкторы, сеттеры и геттеры.
package ChainOfResponsibilityExample; public class FuelQuantityPlan { private String flightPath; private float flyingHours; private float fuelQuantity; public void setFuelQuantity(float fuelQuantity) { this.fuelQuantity = fuelQuantity; } public FuelQuantityPlan(String flightPath, float flyingHours) { this.flightPath = flightPath; this.flyingHours = flyingHours; } public String getFlightPath() { return flightPath; } public float getFlyingHours() { return flyingHours; } public float getFuelQuantity() { return fuelQuantity; } }
Шаг 3: Создайте классы-приемники и сделайте класс-обработчик родительским классом.
Примечание. Здесь показаны только первый и последний классы получателей, чтобы уменьшить сложность и объем статьи.
package ChainOfResponsibilityExample; public class ShortRange extends FuelQuantityHandler{ @Override public float calculateMaxFuelQuantity(FuelQuantityPlan fuelQuantityPlan) { fuelQuantityPlan.setFuelQuantity((float) (fuelQuantityPlan.getFlyingHours() * 0.2)); System.out.println("Short Range Fuel Quantity Added"); if ( fuelQuantityPlan.getFlyingHours() <= 5) { return fuelQuantityPlan.getFuelQuantity(); } else { return successor.calculateMaxFuelQuantity(fuelQuantityPlan); } } } package ChainOfResponsibilityExample; public class IntercontinentalRange extends FuelQuantityHandler{ @Override public float calculateMaxFuelQuantity(FuelQuantityPlan fuelQuantityPlan) { fuelQuantityPlan.setFuelQuantity((float) (fuelQuantityPlan.getFuelQuantity() + (fuelQuantityPlan.getFlyingHours() - 12.5) * 0.8)); System.out.println("Intercontinental Range Fuel Quantity Added"); return fuelQuantityPlan.getFuelQuantity(); } }
Сначала вычислите соответствующее значение, после этого проверьте, подходит ли это значение для расчета следующего количества топлива. Если оно подходит, текущий экземпляр класса плана количества топлива будет передан в качестве аргумента для метода «calculateMaxFuelQuantity» экземпляра-преемника.
В последнем классе, который подпадает под категорию класса приемника, нам нужно только рассчитать окончательное количество топлива и передать количество топлива.
Шаг 4: Создайте начальный класс, который инициирует шаблон проектирования цепочки ответственности.
Примечание. Это необязательный шаг, разработчик может инициировать цепочку ответственности, используя первый класс получателя.
package ChainOfResponsibilityExample; public class MinimumFuelQuantity extends FuelQuantityHandler{ @Override public float calculateMaxFuelQuantity(FuelQuantityPlan fuelQuantityPlan) { return successor.calculateMaxFuelQuantity(fuelQuantityPlan); } }
Шаг 5: Создайте основной метод и посмотрите, как работает шаблон проектирования цепочки ответственности.
package ChainOfResponsibilityExample; public class Application { public static void main(String[] args) { ShortRange shortRange = new ShortRange(); MidRange midRange = new MidRange(); LongRange longRange = new LongRange(); IntercontinentalRange intercontinentalRange = new IntercontinentalRange(); MinimumFuelQuantity minimumFuelQuantity = new MinimumFuelQuantity(); minimumFuelQuantity.setSuccessor(shortRange); shortRange.setSuccessor(midRange); midRange.setSuccessor(longRange); longRange.setSuccessor(intercontinentalRange); FuelQuantityPlan fuelQuantityPlan = new FuelQuantityPlan("AB1", 2.0f); System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan)); FuelQuantityPlan fuelQuantityPlan1 = new FuelQuantityPlan("AB2", 6.0f); System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan1)); FuelQuantityPlan fuelQuantityPlan2 = new FuelQuantityPlan("AB3", 8.0f); System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan2)); FuelQuantityPlan fuelQuantityPlan3 = new FuelQuantityPlan("AB4", 14.0f); System.out.println(minimumFuelQuantity.calculateMaxFuelQuantity(fuelQuantityPlan3)); } }
В этом классе объявляется и инициализируется каждый экземпляр класса получателя, включая исходный класс, если он реализован разработчиком.
Он устанавливает преемников для каждого класса получателя.
Преимущества шаблона проектирования цепочки ответственности:
· Разработчик может изменить структуру или порядок получателей и отправителей, не манипулируя базовой реализацией. В результате этого повышается гибкость возложенных на объект обязанностей.
Недостатки шаблона проектирования цепочки ответственности:
· Не гарантируется получение объекта соответствующим получателем.
· Производительность системы будет затронута, но и отладка кода не является легкой задачей.
Я надеюсь, что теперь вы можете понять, что такое шаблон проектирования цепочки ответственности, как реализовать цепочку ответственности, когда ее использовать и каковы ее преимущества и недостатки.