Автор оригинала: Pankaj Kumar.
Шаблон проектирования цепочки ответственности является одним из поведенческих шаблонов проектирования .
Схема проектирования цепочки ответственности
Шаблон цепочки ответственности используется для достижения слабой связи при разработке программного обеспечения, когда запрос от клиента передается цепочке объектов для их обработки. Затем объект в цепочке сам решит, кто будет обрабатывать запрос и требуется ли отправлять запрос следующему объекту в цепочке или нет.
Пример шаблона цепочки ответственности в JDK
Давайте рассмотрим пример шаблона цепочки ответственности в JDK, а затем перейдем к реализации примера этого шаблона в реальной жизни. Мы знаем, что в коде блока try-catch может быть несколько блоков catch. Здесь каждый блок catch является своего рода процессором для обработки этого конкретного исключения.
Поэтому, когда в блоке try возникает какое-либо исключение, его отправляют в первый блок catch для обработки. Если блок catch не может его обработать, он перенаправляет запрос следующему объекту в цепочке, т. е. следующему блоку catch. Если даже последний блок catch не может его обработать, исключение выбрасывается за пределы цепочки вызывающей программе.
Пример шаблона проектирования цепочки ответственности
Одним из замечательных примеров схемы цепочки ответственности является Банкомат|/. Пользователь вводит сумму, подлежащую выдаче, и машина выдает сумму в виде определенных валютных купюр, таких как 50$, 20$, 10$ и т.д.
Если пользователь вводит сумму, не кратную 10, возникает ошибка. Мы будем использовать шаблон цепочки ответственности для реализации этого решения. Цепочка обработает запрос в том же порядке, что и на изображении ниже.
Обратите внимание, что мы можем легко реализовать это решение в одной программе, но тогда сложность возрастет, и решение будет тесно связано. Поэтому мы создадим цепочку распределительных систем для выдачи купюр по 50, 20 и 10 долларов.
Шаблон проектирования цепочки ответственности – Базовые классы и интерфейс
Мы можем создать класс Валюта
, в котором будет храниться сумма, подлежащая распределению и используемая реализациями цепочки.
Мы можем создать класс || Валюта||, в котором будет храниться сумма, подлежащая распределению и используемая реализациями цепочки.
package com.journaldev.design.chainofresponsibility; public class Currency { private int amount; public Currency(int amt){ this.amount=amt; } public int getAmount(){ return this.amount; } }
Базовый интерфейс должен иметь метод для определения следующего процессора в цепочке и метод, который будет обрабатывать запрос. Наш интерфейс выдачи банкоматов будет выглядеть следующим образом.
Базовый интерфейс должен иметь метод для определения следующего процессора в цепочке и метод, который будет обрабатывать запрос. Наш интерфейс выдачи банкоматов будет выглядеть следующим образом.
package com.journaldev.design.chainofresponsibility; public interface DispenseChain { void setNextChain(DispenseChain nextChain); void dispense(Currency cur); }
Цепочка обязанностей Шаблон – Цепочка реализации
Нам нужно создать различные классы процессоров, которые будут реализовывать интерфейс Цепочки распределения
и обеспечивать реализацию методов распределения. Поскольку мы разрабатываем нашу систему для работы с тремя типами валютных купюр – 50$, 20$ и 10$, мы создадим три конкретные реализации.
Нам нужно создать различные классы процессоров, которые будут реализовывать интерфейс || Цепочки распределения || и обеспечивать реализацию методов распределения. Поскольку мы разрабатываем нашу систему для работы с тремя типами валютных купюр – 50$, 20$ и 10$, мы создадим три конкретные реализации.
package com.journaldev.design.chainofresponsibility; public class Dollar50Dispenser implements DispenseChain { private DispenseChain chain; @Override public void setNextChain(DispenseChain nextChain) { this.chain=nextChain; } @Override public void dispense(Currency cur) { if(cur.getAmount() >= 50){ int num = cur.getAmount()/50; int remainder = cur.getAmount() % 50; System.out.println("Dispensing "+num+" 50$ note"); if(remainder !=0) this.chain.dispense(new Currency(remainder)); }else{ this.chain.dispense(cur); } } }
Нам нужно создать различные классы процессоров, которые будут реализовывать интерфейс || Цепочки распределения || и обеспечивать реализацию методов распределения. Поскольку мы разрабатываем нашу систему для работы с тремя типами валютных купюр – 50$, 20$ и 10$, мы создадим три конкретные реализации.
package com.journaldev.design.chainofresponsibility; public class Dollar20Dispenser implements DispenseChain{ private DispenseChain chain; @Override public void setNextChain(DispenseChain nextChain) { this.chain=nextChain; } @Override public void dispense(Currency cur) { if(cur.getAmount() >= 20){ int num = cur.getAmount()/20; int remainder = cur.getAmount() % 20; System.out.println("Dispensing "+num+" 20$ note"); if(remainder !=0) this.chain.dispense(new Currency(remainder)); }else{ this.chain.dispense(cur); } } }
Нам нужно создать различные классы процессоров, которые будут реализовывать интерфейс || Цепочки распределения || и обеспечивать реализацию методов распределения. Поскольку мы разрабатываем нашу систему для работы с тремя типами валютных купюр – 50$, 20$ и 10$, мы создадим три конкретные реализации.
package com.journaldev.design.chainofresponsibility; public class Dollar10Dispenser implements DispenseChain { private DispenseChain chain; @Override public void setNextChain(DispenseChain nextChain) { this.chain=nextChain; } @Override public void dispense(Currency cur) { if(cur.getAmount() >= 10){ int num = cur.getAmount()/10; int remainder = cur.getAmount() % 10; System.out.println("Dispensing "+num+" 10$ note"); if(remainder !=0) this.chain.dispense(new Currency(remainder)); }else{ this.chain.dispense(cur); } } }
Важным моментом, который следует отметить здесь, является реализация метода распределения. Вы заметите, что каждая реализация пытается обработать запрос, и, исходя из суммы, она может обработать некоторую или полную его часть.
Если один из участников цепочки не может обработать его полностью, он отправляет запрос следующему процессору в цепочке для обработки оставшегося запроса. Если процессор не в состоянии что-либо обработать, он просто пересылает тот же запрос в следующую цепочку.
Цепочка обязанностей Шаблон проектирования – Создание цепочки
Это очень важный шаг, и мы должны тщательно создавать цепочку, иначе процессор может вообще не получать никаких запросов. Например, в нашей реализации , если мы сохраним первую цепочку процессоров как Распределитель 10 долларов
, а затем Распределитель 20 долларов
, то запрос никогда не будет перенаправлен на второй процессор, и цепочка станет бесполезной.
Вот наша реализация банкомата для обработки запрошенной пользователем суммы.
Вот наша реализация банкомата для обработки запрошенной пользователем суммы.
package com.journaldev.design.chainofresponsibility; import java.util.Scanner; public class ATMDispenseChain { private DispenseChain c1; public ATMDispenseChain() { // initialize the chain this.c1 = new Dollar50Dispenser(); DispenseChain c2 = new Dollar20Dispenser(); DispenseChain c3 = new Dollar10Dispenser(); // set the chain of responsibility c1.setNextChain(c2); c2.setNextChain(c3); } public static void main(String[] args) { ATMDispenseChain atmDispenser = new ATMDispenseChain(); while (true) { int amount = 0; System.out.println("Enter amount to dispense"); Scanner input = new Scanner(System.in); amount = input.nextInt(); if (amount % 10 != 0) { System.out.println("Amount should be in multiple of 10s."); return; } // process the request atmDispenser.c1.dispense(new Currency(amount)); } } }
Когда мы запускаем приложение выше, мы получаем результат, как показано ниже.
Enter amount to dispense 530 Dispensing 10 50$ note Dispensing 1 20$ note Dispensing 1 10$ note Enter amount to dispense 100 Dispensing 2 50$ note Enter amount to dispense 120 Dispensing 2 50$ note Dispensing 1 20$ note Enter amount to dispense 15 Amount should be in multiple of 10s.
Цепочка обязанностей Схема классов шаблона проектирования
Наш пример реализации шаблона проектирования цепочки ответственности в банкоматах выглядит следующим образом.
Цепочка ответственности Шаблон проектирования Важные моменты
- Клиент не знает, какая часть цепочки будет обрабатывать запрос, и он отправит запрос первому объекту в цепочке. Например, в нашей программе Цепочка выдачи банкоматов не знает, кто обрабатывает запрос на выдачу введенной суммы.
- Каждый объект в цепочке будет иметь свою собственную реализацию для обработки запроса, полного или частичного, или для отправки его следующему объекту в цепочке.
- Каждый объект в цепочке должен иметь ссылку на следующий объект в цепочке для пересылки запроса, что достигается с помощью java-композиции .
- Тщательное создание цепочки очень важно, иначе может возникнуть ситуация, когда запрос никогда не будет перенаправлен определенному процессору или в цепочке нет объектов, способных обработать запрос. В моей реализации я добавил проверку введенной пользователем суммы, чтобы убедиться, что она будет полностью обработана всеми процессорами, но мы можем не проверять ее и выдавать исключение, если запрос достигнет последнего объекта и в цепочке больше нет объектов для пересылки запроса. Это дизайнерское решение.
- Шаблон проектирования цепочки ответственности хорош для достижения слабой связи, но он сопряжен с большим количеством классов реализации и проблемами обслуживания, если большая часть кода является общей для всех реализаций.
Примеры шаблонов цепочки ответственности в JDK
- java.util.ведение журнала.Регистратор#журнал()
- javax.сервлет.Фильтр#doFilter()
Это все для шаблона дизайна цепочки ответственности, я надеюсь, что он вам понравился и он сможет прояснить ваше понимание этого шаблона дизайна.