Статьи

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

Эта статья является частью нашего курса Академии под названием « Шаблоны проектирования Java» .

В этом курсе вы изучите огромное количество шаблонов проектирования и увидите, как они реализуются и используются в Java. Вы поймете причины, почему шаблоны так важны, и узнаете, когда и как применять каждый из них. Проверьте это здесь !

1. Схема цепочки ответственности

Паттерн Chain of Responsibility — это паттерн поведения, в котором группа объектов объединяется в последовательность, и ответственность (запрос) предоставляется для обработки этой группой. Если объект в группе может обработать конкретный запрос, он делает это и возвращает соответствующий ответ. В противном случае он направляет запрос последующему объекту в группе.

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

Давайте рассмотрим сценарий из реальной жизни. У вашей компании есть контракт на предоставление аналитической заявки для медицинской компании. Приложение расскажет пользователю о конкретной проблеме со здоровьем, ее истории, лечении, лекарствах, опросе человека, страдающего от него и т. Д., Обо всем, что необходимо знать о нем. Для этого ваша компания получает огромное количество данных. Данные могут быть в любом формате, это могут быть текстовые файлы, документы doc, отличные файлы, аудио, изображения, видео, все, что вы можете себе представить.

Теперь ваша задача — сохранить эти данные в базе данных компании. Пользователи будут предоставлять данные в любом формате, и вы должны предоставить им единый интерфейс для загрузки данных в базу данных. Пользователь не заинтересован, даже не подозревает, чтобы знать, как вы сохраняете различные неструктурированные данные?

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

Для решения этой проблемы вы можете использовать шаблон проектирования «Цепочка ответственности». Вы можете создавать разные объекты, которые обрабатывают разные форматы данных и объединяют их в цепочку. Когда запрос приходит к одному объекту, он проверяет, может ли он обрабатывать и обрабатывать специфический формат файла. Если это возможно, он обработает это; в противном случае он перенаправит его на следующий объект, связанный с ним. Этот шаблон проектирования также отделяет пользователя от объекта, который обслуживает запрос; пользователь не знает, какой объект фактически обслуживает его запрос.

Прежде чем решить проблему, давайте сначала узнаем больше о шаблоне проектирования «Цепочка ответственности».

2. Что такое схема цепочки ответственности

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

Этот шаблон предназначен для соединения объектов в цепочке уведомлений; когда уведомление перемещается по цепочке, оно обрабатывается первым объектом, который настроен для работы с конкретным уведомлением.

Когда существует более одного объекта, который может обработать или выполнить запрос клиента, шаблон рекомендует дать каждому из этих объектов возможность обработать запрос в некотором последовательном порядке. Применяя шаблон в таком случае, каждый из этих потенциальных обработчиков может быть организован в форме цепочки, причем каждый объект имеет ссылку на следующий объект в цепочке. Первый объект в цепочке получает запрос и решает либо обработать запрос, либо передать его следующему объекту в цепочке. Запрос проходит через все объекты в цепочке один за другим, пока запрос не будет обработан одним из обработчиков в цепочке или запрос не достигнет конца цепочки без обработки.

фигура 1

фигура 1

укротитель

  1. Определяет интерфейс для обработки запросов.
  2. (Необязательно) Реализует ссылку-преемник.

ConcreteHandler

  1. Обрабатывает запросы, за которые он отвечает.
  2. Может получить доступ к его преемнику.
  3. Если ConcreteHandler может обработать запрос, он делает это; в противном случае он направляет запрос своему преемнику.

клиент

  1. Инициирует запрос к объекту ConcreteHandler в цепочке.

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

3. Реализация цепочки ответственности

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

1
2
3
4
5
6
7
8
package com.javacodegeeks.patterns.chainofresponsibility;
 
public interface Handler {
 
    public void setHandler(Handler handler);
    public void process(File file);
    public String getHandlerName();
}

Приведенный выше интерфейс содержит два основных метода, setHandler и методы процесса. setHandler используется для установки следующего обработчика в цепочке, тогда как; метод process используется для обработки запроса, только если обработчик может обработать запрос. По желанию, у нас есть метод getHandlerName который используется для возврата имени обработчика.

Обработчики предназначены для обработки файлов, которые содержат данные. Конкретный обработчик проверяет, может ли он обрабатывать файл, проверяя тип файла, в противном случае пересылается следующему обработчику в цепочке.

Класс File выглядит следующим образом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class File {
 
    private final String fileName;
    private final String fileType;
    private final String filePath;
 
    public File(String fileName, String fileType, String filePath){
        this.fileName = fileName;
        this.fileType = fileType;
        this.filePath = filePath;
    }
 
    public String getFileName() {
        return fileName;
    }
 
    public String getFileType() {
        return fileType;
    }
 
    public String getFilePath() {
        return filePath;
    }
 
}

Класс File создает простые файловые объекты, которые содержат имя файла, тип файла и путь к файлу. Тип файла будет использоваться обработчиком для проверки, может ли файл обрабатываться ими или нет. Если обработчик может, он обработает и сохранит его или перенаправит его следующему обработчику в цепочке.

Давайте посмотрим некоторые конкретные обработчики сейчас.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class TextFileHandler implements Handler {
 
    private Handler handler;
    private String handlerName;
 
    public TextFileHandler(String handlerName){
        this.handlerName = handlerName;
    }
 
    @Override
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
 
    @Override
    public void process(File file) {
 
        if(file.getFileType().equals("text")){
            System.out.println("Process and saving text file... by "+handlerName);
        }else if(handler!=null){
            System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
            handler.process(file);
        }else{
            System.out.println("File not supported");
        }
 
    }
 
    @Override
    public String getHandlerName() {
        return handlerName;
    }
}

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

Другие обработчики похожи на описанный выше обработчик.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class DocFileHandler implements Handler{
 
    private Handler handler;
    private String handlerName;
 
    public DocFileHandler(String handlerName){
        this.handlerName = handlerName;
    }
 
    @Override
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
 
    @Override
    public void process(File file) {
 
        if(file.getFileType().equals("doc")){
            System.out.println("Process and saving doc file... by "+handlerName);
        }else if(handler!=null){
            System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
            handler.process(file);
        }else{
            System.out.println("File not supported");
        }
 
    }
 
    @Override
    public String getHandlerName() {
        return handlerName;
    }
 
}
 
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class AudioFileHandler implements Handler {
 
    private Handler handler;
    private String handlerName;
 
    public AudioFileHandler(String handlerName){
        this.handlerName = handlerName;
    }
 
    @Override
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
 
    @Override
    public void process(File file) {
 
        if(file.getFileType().equals("audio")){
            System.out.println("Process and saving audio file... by "+handlerName);
        }else if(handler!=null){
            System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
            handler.process(file);
        }else{
            System.out.println("File not supported");
        }
 
    }
 
    @Override
    public String getHandlerName() {
        return handlerName;
    }
 
}
 
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class ExcelFileHandler implements Handler{
 
    private Handler handler;
    private String handlerName;
 
    public ExcelFileHandler(String handlerName){
        this.handlerName = handlerName;
    }
 
    @Override
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
 
    @Override
    public void process(File file) {
 
        if(file.getFileType().equals("excel")){
            System.out.println("Process and saving excel file... by "+handlerName);
        }else if(handler!=null){
            System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
            handler.process(file);
        }else{
            System.out.println("File not supported");
        }
 
    }
 
    @Override
    public String getHandlerName() {
        return handlerName;
    }
}
 
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class ImageFileHandler implements Handler {
 
    private Handler handler;
    private String handlerName;
 
    public ImageFileHandler(String handlerName){
        this.handlerName = handlerName;
    }
 
    @Override
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
 
    @Override
    public void process(File file) {
 
        if(file.getFileType().equals("image")){
            System.out.println("Process and saving image file... by "+handlerName);
        }else if(handler!=null){
            System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
            handler.process(file);
        }else{
            System.out.println("File not supported");
        }
 
    }
 
    @Override
    public String getHandlerName() {
        return handlerName;
    }
 
}
 
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class VideoFileHandler implements Handler {
 
    private Handler handler;
    private String handlerName;
 
    public VideoFileHandler(String handlerName){
        this.handlerName = handlerName;
    }
 
    @Override
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
 
    @Override
    public void process(File file) {
 
        if(file.getFileType().equals("video")){
            System.out.println("Process and saving video file... by "+handlerName);
        }else if(handler!=null){
            System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
            handler.process(file);
        }else{
            System.out.println("File not supported");
        }
 
    }
 
    @Override
    public String getHandlerName() {
        return handlerName;
    }
}

Теперь давайте проверим код выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.javacodegeeks.patterns.chainofresponsibility;
 
public class TestChainofResponsibility {
 
    public static void main(String[] args) {
        File file = null;
        Handler textHandler = new TextFileHandler("Text Handler");
        Handler docHandler = new DocFileHandler("Doc Handler");
        Handler excelHandler = new ExcelFileHandler("Excel Handler");
        Handler audioHandler = new AudioFileHandler("Audio Handler");
        Handler videoHandler = new VideoFileHandler("Video Handler");
        Handler imageHandler = new ImageFileHandler("Image Handler");
 
        textHandler.setHandler(docHandler);
        docHandler.setHandler(excelHandler);
        excelHandler.setHandler(audioHandler);
        audioHandler.setHandler(videoHandler);
        videoHandler.setHandler(imageHandler);
 
        file = new File("Abc.mp3", "audio", "C:");
        textHandler.process(file);
 
        System.out.println("---------------------------------");
 
        file = new File("Abc.jpg", "video", "C:");
        textHandler.process(file);
 
        System.out.println("---------------------------------");
 
        file = new File("Abc.doc", "doc", "C:");
        textHandler.process(file);
 
        System.out.println("---------------------------------");
 
        file = new File("Abc.bat", "bat", "C:");
        textHandler.process(file);
    }
 
}

Вышеуказанная программа будет иметь следующий вывод.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
Text Handler fowards request to Doc Handler
Doc Handler fowards request to Excel Handler
Excel Handler fowards request to Audio Handler
Process and saving audio file... by Audio Handler
---------------------------------
Text Handler fowards request to Doc Handler
Doc Handler fowards request to Excel Handler
Excel Handler fowards request to Audio Handler
Audio Handler fowards request to Video Handler
Process and saving video file... by Video Handler
---------------------------------
Text Handler fowards request to Doc Handler
Process and saving doc file... by Doc Handler
---------------------------------
Text Handler fowards request to Doc Handler
Doc Handler fowards request to Excel Handler
Excel Handler fowards request to Audio Handler
Audio Handler fowards request to Video Handler
Video Handler fowards request to Image Handler
File not supported

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

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

Кроме того, обратите внимание, мы не создали обработчик для обработки файла bat. Таким образом, он проходит через все обработчики и приводит к выводу «Файл не поддерживается».

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

4. Когда использовать шаблон цепочки ответственности

Использовать цепь ответственности, когда

  1. Больше чем один объект может обработать запрос, и обработчик не известен априори. Обработчик должен быть установлен автоматически.
  2. Вы хотите отправить запрос к одному из нескольких объектов без явного указания получателя.
  3. Набор объектов, которые могут обрабатывать запрос, должен указываться динамически.

5. Цепочка ответственности в JDK

Ниже приведено использование шаблона цепочки ответственности в Java.

  1. java.util.logging.Logger#log()
  2. javax.servlet.Filter#doFilter()

6. Загрузите исходный код

Это был урок по модели цепочки ответственности. Вы можете скачать исходный код здесь: ChainofResponsibility-Project