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) {
    ...
  }
}

 


Споріднені теми