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

Ситуация, в которой разработчики могут использовать шаблон цепочки ответственности?

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

· Несколько объектов, определенных во время выполнения, являются кандидатами для обработки запроса

· Когда разработчик не хочет явно указывать обработчики в коде

· Когда разработчик хочет сделать запрос к одному из нескольких объектов без явного указания получателя.

Как вы можете видеть на приведенной выше диаграмме 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));
    }
}

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

Он устанавливает преемников для каждого класса получателя.

Преимущества шаблона проектирования цепочки ответственности:

· Разработчик может изменить структуру или порядок получателей и отправителей, не манипулируя базовой реализацией. В результате этого повышается гибкость возложенных на объект обязанностей.

Недостатки шаблона проектирования цепочки ответственности:

· Не гарантируется получение объекта соответствующим получателем.

· Производительность системы будет затронута, но и отладка кода не является легкой задачей.

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