Patterns. Паттерн Chain of Responsibility. Пример реализации на Java

Паттерн Chain of Responsibility. Пример реализации на Java

В данной теме приводится пример реализации паттерна Chain of Responsibility на языке Java для структуры, содержащей иерархию классов-обработчиков. Перед изучением темы рекомендуется ознакомиться с общими сведениями о паттерне Chain of Responsibility, которые описываются в теме:


Содержание


Поиск на других ресурсах:

1. Условие задачи

Пусть заданы 4 класса ConcreteHandler1, ConcreteHandler2, ConcreteHandler3, ConcreteHandler4. Два класса ConcreteHandler1, ConcreteHandler2 унаследованы от класса Handlers1, как показано на рисунке 1.

Паттерн Chain of Responsibility. Иерархия классов

Рисунок 1. Иерархия классов ConcreteHandler1, ConcreteHandler2, Handlers1

Необходимо разработать приложение на языке Java, в котором вышеприведенные классы обрабатывают единственный запрос от клиента и организованы в цепочку на основе паттерна Chain of Responsibility.

Продемонстрировать работу классов-обработчиков, реализовав диспетчеризацию.

 

2. Решение
2.1. Создание метода ChainMethod()

Прежде всего в каждый класс вводится метод ChainMethod(), являющийся непосредственно обработчиком. Это означает, что этот метод обрабатывает запрос клиента. В нашем случае метод будет выводить сообщения об имени класса и имени метода. К примеру, для класса ConcreteHandler1 метод будет выводить сообщение

ConcreteHandler1.ChainMethod

Метод ChainMethod() будет передаваться по цепочке для его обработки одним из классов-обработчиков.

 

2.2. Построение структуры классов, реализующих паттерн Chain of Responsibility. Рисунок

На основе общей структуры паттерна Chain of Responsibility и ввода метода ChainMethod() разрабатывается структура, изображенная на рисунке 2.

Структура паттерна Chain of Responsibility для иерархии классов

Рисунок 2. Структура паттерна Chain of Responsibility для иерархии классов, реализованных в текущем примере

На следующих шагах разрабатываются классы, изображенные на рисунке 1.

 

2.3. Класс Handler

Базовым классом в иерархии есть класс Handler. В этом классе создается весь функционал, необходимый для реализации паттерна Chain of Responsibility. В классе объявляются:

  • внутреннее поле successor, являющееся ссылкой на преемника. Эта ссылка используется в каждом классе-обработчике ConcreteHandler1, ConcreteHandler2, ConcreteHandler3. В классе ConcreteHandler4 эта ссылка установлена ​​в null, поскольку этот класс является последним в цепочке обработчиков;
  • внутреннее поле numHandler, являющееся номером обработчика. На основе этого номера в методе DoRequest() определяется, можно ли обрабатывать запрос обработчиком;
  • три конструктора, предназначенных для создания экземпляра класса-обработчика на основе указания (не указания) данных о преемнике (successor) и номере обработчика (numHandler);
  • метод ChainMethod(). Этот метод напрямую обрабатывает запрос;
  • метод DoRequest(), который на основе значения numHandler определяет, обрабатывать ли запрос. Если значение numHandler равно -1, то запрос не обрабатывается;
  • метод SetHandler() – реализует установку обработчика (successor) и его номера (numHandler) после того, как создан экземпляр одного из классов-обработчиков.

Текст класса Handler следующий.

// Класс, являющийся единственным интерфейсом для объектов, обрабатывающих запрос.
// Класс содержит методы, которые есть общими для обработки.
class Handler {
  // 1. Внутренние переменные
  // 1.1. Ссылка на преемника
  private Handler successor = null;

  // 1.2. Номер обработчика, обрабатывающего запрос
  private int numHandler = -1;

  // 2. Конструкторы
  // 2.1. Конструктор без параметров
  public Handler() {
    this(null, -1);
  }

  // 2.2. Конструктор с 1 параметром
  public Handler(Handler _successor) {
    this(_successor, -1);
  }

  // 2.3. Конструктор с 2 параметрами
  public Handler(Handler _successor, int _numHandler) {
    successor = _successor;
    numHandler = _numHandler;
  }

  // Метод, который двигается по цепочке.
  // Данный метод обрабатывает запрос.
  public void ChainMethod() {
    if (successor!=null)
      successor.ChainMethod();
  }

  // Метод, определяющий, нужно ли обрабатывать запрос.
  public boolean DoRequest() {
    return numHandler != -1;
  }

  // Метод, который устанавливает обработчика и возможность обработки
  public void SetHandler(Handler _successor, int _numHandler) {
    successor = _successor;
    numHandler = _numHandler;
  }
}

 

2.4. Класс Handlers1

Класс Handlers1 является суперклассом для классов-обработчиков ConcreteHandler1 и ConcreteHandler2. Для корректной реализации паттерна Chain of Responsibility этот класс должен быть унаследован от класса Handler. В контексте нашей задачи класс содержит конструктор, который вызывает конструктор суперкласса Handler.

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

Текст класса Handlers1 на языке Java следующий.

// Промежуточный класс, являющийся базовым для конкретных обработчиков
// ConcreteHandler1, ConcreteHandler2
class Handlers1 extends Handler {

  // Конструктор класса
  public Handlers1(Handler _parent, int _numHandler) {
    // Вызвать конструктор базового класса
    super(_parent, _numHandler);
  }
}

 

2.5. Классы-обработчики ConcreteHandler1, ConcreteHandler2

Классы-обработчики ConcreteHandler1, ConcreteHandler2 унаследованы от класса Handlers1. Поэтому конструкторы этих классов перенаправляют наследника на конструктор класса Handlers1.

Также классы содержат метод ChainMethod(), реализующий обработку.

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

Текст классов ConcreteHandler1, ConcreteHandler2 дается ниже.

// Конкретный обработчик - 1
class ConcreteHandler1 extends Handlers1 {

  // Конструктор - вызывает конструктор базового класса Handlers1
  public ConcreteHandler1(Handler _parent, int _numHandler) {
    // Вызвать конструктор суперкласса
    super(_parent, _numHandler);
  }

  // Конкретный метод обработки
  public void ChainMethod() {
    // Проверка, обрабатывает ли обработчик данный запрос
    if (super.DoRequest()) {

      // Если обрабатывает, то выполнить соответствующие действия
      System.out.println("ConcreteHandler1.ChainMethod");
    }
    else {
      super.ChainMethod();
    }
  }
}

//Конкретный обработчик - 2
class ConcreteHandler2 extends Handlers1 {

  public ConcreteHandler2(Handler _parent, int _numHandler) {
    // Вызвать конструктор суперкласса
    super(_parent, _numHandler);
  }

  // Конкретный метод обработки
  public void ChainMethod() {
    // Проверка, обрабатывает ли обработчик данный запрос
    if (super.DoRequest()) {
      // Если обрабатывает, то выполнить соответствующие действия
      System.out.println("ConcreteHandler2.ChainMethod");
    }
    else {
      super.ChainMethod();
    }
  }
}

 

2.6. Класс-обработчик ConcreteHandler3

Класс-обработчик ConcreteHandler3 унаследован от базового класса Handler. Этот класс не является конечным звеном в цепи вызовов.

Программный код класса следующий.

// Конкретный обработчик - 3
// Этот обработчик не входит в иерархию классов, в которой
// базовым является класс Handlers1.
class ConcreteHandler3 extends Handler {

  // Конструктор
  public ConcreteHandler3(Handler _handler, int _numHandler) {
    // Вызвать конструктор суперкласса
    super(_handler, _numHandler);
  }

  // Конкретный метод обработки
  public void ChainMethod() {
    // Проверка, обрабатывает ли обработчик данный запрос
    if (super.DoRequest()) {
      // Если обрабатывает, то выполнить соответствующие действия
      System.out.println("ConcreteHandler3.ChainMethod");
    }
    else
    {
      super.ChainMethod();
    }
  }
}

 

2.7. Класс-обработчик ConcreteHandler4. Завершение цепочки

Класс-обработчик ConcreteHandler4 следует последним в цепочке вызовов. Соответствующим образом производится обработка в методе ChainMethod().

Текст класса следующий.

// Конкретный обработчик - 4
// Этот обработчик не входит в иерархию классов, в которой
// базовым является класс Handlers1.
// Этот обработчик является последним в цепочке обработчиков.
class ConcreteHandler4 extends Handler {

  // Конструктор.
  // Поскольку обработчик является последним в цепочке обработчиков,
  // то ему не нужно передавать ссылку на предыдущего обработчика.
  public ConcreteHandler4(int _numHandler) {
    super(null, _numHandler);
  }

  // Конкретный метод обработки
  public void ChainMethod() {
    // Проверка, обрабатывает ли обработчик данный запрос
    if (super.DoRequest()) {
      // Если обрабатывает, то выполнить соответствующие действия
      System.out.println("ConcreteHandler4.ChainMethod");
    }
    else
    {
      // Если обработчик не обрабатывает данный запрос,
      // то вывести сообщение о том, что в цепочке
      // нет соответствующего обработчика.
      System.out.println("There is no corresponding handler in the chain.");
    }
  }
}

 

2.8. Тест работы классов-обработчиков. Функция main(). Диспетчеризация

В данном примере клиентом выступает класс TrainChain, содержащий функцию main() – точку входа в программу. В функции main() выполняются следующие действия:

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

Для ввода данных с клавиатуры используются возможности класса Scanner, подключаемого с помощью директивы

import java.util.Scanner;

Текст клиентского кода следующий

// Подключить модуль Scanner
import java.util.Scanner;

...

// Клиентский класс
public class TrainChain {

  public static void main(String[] args) {
    // Код клиента.
    // 1. Создать цепочку из 4-х обработчиков.
    //   Обработчики создаются в порядке от последнего до первого.

    // 1.1. Первым создается последний обработчик в цепочке
    ConcreteHandler4 handler4 = new ConcreteHandler4(4); // 4 - номер обработчика

    // 1.2. Добавить следующего обработчика.
    //      Обработчик получает ссылку на предыдущего обработчика handler4
    ConcreteHandler3 handler3 = new ConcreteHandler3((Handler)handler4, 3);

    // 1.3. Добавить обработчиков, являющихся подклассами класса Handlers1
    // 1.3.1. Обработчик, который следует вторым в цепочке.
    ConcreteHandler2 handler2 = new ConcreteHandler2((Handler)handler3, 2);

    // 1.3.2. Обработчик, который следует первым в цепочке.
    ConcreteHandler1 handler1 = new ConcreteHandler1((Handler)handler2, 1);

    // 2. Создать клиента
    //    Клиент указывает на цепочку объектов-обработчиков
    //    client -> handler1 -> handler2 -> handler3 -> handler4
    Handler client = new Handler((Handler)handler1);

    // 3. Ввести с клавиатуры номер обработчика
    System.out.println("numHandler = ");
    Scanner input = new Scanner(System.in);
    int numHandler = input.nextInt();

    // 4. В зависимости от numObject реализовать диспетчеризацию
    switch (numHandler) {
      case 1:
        // Установить обработчика-1 для клиента и вызвать его
        client.SetHandler(handler1, 1);
        client.ChainMethod();
        break;
      case 2:
        // Установить обработчика-2 для клиента и вызвать его
        client.SetHandler(handler2, 2);
        client.ChainMethod();
        break;
      case 3:
        // Установить обработчика-3 для клиента и вызвать его
        client.SetHandler(handler3, 3);
        client.ChainMethod();
        break;
      case 4:
        // Установить обработчика-4 для клиента и вызвать его
        client.SetHandler(handler4, 4);
        client.ChainMethod();
        break;
      default:
        System.out.println("Incorrect handler");
        break;
    }

    // Закрыть поток ввода
    input.close();
  }
}

 

2.9. Сокращенная структура программы

Чтобы получить весь код программы, нужно скопировать программные коды, представленные в пунктах 2.1-2.5 данной темы. В сокращенной форме структура программы имеет вид:

// Подключить модуль Scanner
import java.util.Scanner;

// Класс, который есть единственным интерфейсом для объектов, обрабатывающих запрос
class Handler {
  ...
}

// Промежуточный класс, являющийся базовым для конкретных обработчиков
// ConcreteHandler1, ConcreteHandler2
class Handlers1 extends Handler {
  ...
}

// Конкретный обработчик - 1
class ConcreteHandler1 extends Handlers1 {
  ...
}

// Конкретный обработчик - 2
class ConcreteHandler2 extends Handlers1 {
  ...
}

// Конкретный обработчик - 3
class ConcreteHandler3 extends Handler {
  ...
}

// Конкретный обработчик - 4
class ConcreteHandler4 extends Handler {
  ...
}

// Клиентский класс
public class TrainChain {

  public static void main(String[] args) {
    ...
  }
}

 


Связанные темы