Patterns. Паттерн Builder. Реалізація на Java. Приклад

Паттерн Builder. Реалізація на Java. Приклад

Перед вивченням даної теми рекомендується ознайомитись з наступною темою:

 


Зміст


Структура паттерну Builder:




1. Структура паттерну Builder. Рисунок

Паттерн Builder належить до породжуючих паттернів. Паттерн призначений для породження об’єктів. Структура паттерну Builder зображена на рисунку 1.

Структура паттерну Builder. Узагальнений випадок

Рисунок 1. Структура паттерну Builder. Узагальнений випадок

 

2. Приклад реалізації паттерну Builder на Java. Побудова об’єкту комплексного числа
2.1. Умова задачі. Рисунок, який потрібно реалізувати

У прикладі демонструється реалізація паттерну Builder основою якого служить структура, зображена на рисунку 2. Продуктом виступає клас Complex, що визначає комплексне число яке складається з дійсної та уявної частини. У класі розпоряднику Director в методі Construct() будуються дві частини комплексного числа. Отримане число повертається клієнту в методі GetResult().

Структура паттерну Builder, яка відображає розв’язок задачі

Рисунок 2. Структура паттерну Builder, яка відображає розв’язок задачі

 

2.2. Програмний код розв’язку задачі

Реалізація паттерну Builder на мові Java наступна

// Реалізація паттерну Builder на Java.

// Клас, що є продуктом - комплексне число
class Complex {
  // Частини
  public double re; // дійсна частина комплексного числа
  public double im; // уявна частина комплексного числа
}

// Клас, що реалізує інтерфейс з клієнтом
class Builder{
  // Методи, що передаються клієнту
  void CreateComplex() {}
  void BuildPart1(int part1) {}
  void BuildPart2(int part2) {}
  Complex GetResult() { return null; }
}

// Клас, що є конкретним будівником
class ConcreteBuilder extends Builder {
  // Посилання
  private Complex currentBuilder;

  // Конструктор
  ConcreteBuilder() {
    currentBuilder = null;
  }

  // Перевизначення методів, що визначені в класі Builder
  void CreateComplex() {
    System.out.println("ConcreteBuilder.CreateComplex()");
    currentBuilder = new Complex();
  }

  // Побудувати частину 1
  void BuildPart1(int part1) {
    currentBuilder.re = part1;
  }

  // Побудувати частину 2
  void BuildPart2(int part2) {
    currentBuilder.im = part2;
  }

  // Повернути комплексне число для клієнта
  Complex GetResult() {
    return currentBuilder;
  }
}

// Клас-розпорядник
class Director {
  // Метод, що конструює частини,
  // отримує посилання на клас, що реалізує інтерфейс з клієнтом
  void Construct(Builder builder) {
    // Створити продукт
    builder.CreateComplex();

    // Побудувати частину 1
    builder.BuildPart1(15);

    // Побудувати частину 2
    builder.BuildPart2(30);
  }
}

public class TestBuilder {
  public static void main(String[] args) {
    // Функція main() виступає в ролі клієнта
    // 1. Оголосити посилання на комплексне число (продукт)
    Complex C;

    // 2. Створити конкретний екземпляр класу ConcreteBuilder
    ConcreteBuilder B = new ConcreteBuilder();

    // 3. Створити клас-розпорядник і зконфігурувати його продуктом B
    Director D = new Director();
    D.Construct(B);

    // 4. Передати створений продукт клієнту
    C = B.GetResult();

    // 5. Вивести значення комплексного числа (для перевірки)
    System.out.println("C.re = " + C.re);
    System.out.println("C.im = " + C.im);
  }
}

 

2.3. Пояснення до розв’язку

Пояснимо деякі фрагменти коду. Результатом роботи паттерну Builder повинен бути об’єкт класу. У нашому випадку результатом роботи паттерну є об’єкт класу Complex. За бажанням, можна замінити клас Complex на власний.

Для зв’язку з клієнтом використовується клас Builder. У цьому класі є метод GetResult(), який повертає клієнту побудований об’єкт типу Complex. Це є результат роботи паттерну.

У класі-розпоряднику Director оголошується один єдиний метод Construct(), який створює екземпляр класу Builder та будує його частини – методи BuildPart1() та BuildPart2(). У нашому випадку частинами є дійсна частина комплексного числа (змінна re класу Complex) та уявна частина комплексного числа (змінна im класу Complex). Метод Construct() викликається клієнтом для побудови об’єкту. За бажанням, можна створити декілька методів на кшталт методу Construct(). Тут допускаються різні варіації для отримання необхідного об’єкту.

Конкретний екземпляр (об’єкт) класу Complex безпосередньо створюється в класі ConcreteBuilder і через механізм віртуальних функцій (динамічний поліморфізм) повертається клієнту у вигляді посилання на клас Builder. Завдяки поліморфізму та спадковості до структури можна додавати успадковані з класу Builder класи-будівники (ConcreteBuilder2, ConcreteBuilder3 і т.д.) які будуть будувати (створювати) різноманітні продукти.

 

2.4. Результат роботи програми

Після запуску програма видасть наступний результат

ConcreteBuilder.CreateComplex()
C.re = 15.0
C.im = 30.0

 

3. Використання паттерну Builder для двох конкретних будівників. Розв’язок на Java

У паттерні Builder кількість класів конкретних будівників (ConcreteBuilder) може бути декілька. У даному прикладі продемонстровано використання двох конкретних будівників.

3.1. Умова задачі

Використовуючи структуру паттерну Builder розробити побудову об’єктів наступних класів:

  • класу, що містить засоби конвертування рядка з двійкової системи у десяткову;
  • класу, що містить засоби конвертування рядка з вісімкової системи у десяткову.

Продуктом класів є конвертований рядок (тип String).

 

3.2. Структурна схема розв’язку задачі

Першим кроком рішення задачі є побудова структурної схеми. У нашому випадку структурна схема зображена на рисунку 3.

Паттерн Builder. Структурна схема рішення задачі

Рисунок 3. Структурна схема рішення задачі

Як видно зі схеми, розв’язок задачі побудований на наступних класах:

  • Builder – клас, що реалізує інтерфейс з клієнтом;
  • Director – клас розпорядник. Містить метод Construct(), який будує об’єкт типу Builder;
  • Convert_2_to_10 – клас, що містить засоби конвертування рядка з двійкової системи в десяткову;
  • Convert_8_to_10 – клас, що містить засоби конвертування рядка з вісімкової системи в десяткову.

У програмі також використовується клас TestBuilder, який містить функцію main(), яка демонструє роботу клієнта.

 

3.3. Програмний код розв’язку задачі

На мові Java програмний код розв’язку задачі наступний.

// Реалізація паттерну Builder на Java.

// Клас, що реалізує інтерфейс з клієнтом
interface Builder {
  void Convert(String str2);
  String GetResult();
}

// Клас, що містить засоби конвертування числа
// з двійкової системи в десяткову
class Convert_2_to_10 implements Builder {
  private String str10; // Число у десятковій системі - конвертоване число

  // Метод конвертування Convert() - отримує число у вигляді рядка
  // у двійковій системі числення
  public void Convert(String str2) {
    // Потрібно конвертувати str2 в str10
    // 1. Перевірка, чи str2 коректне, якщо ні, то конвертувати в пустий рядок
    for (int i=0; i<str2.length(); i++)
      if (!((str2.charAt(i)=='0') || (str2.charAt(i)=='1'))) {
        str10 = "Error. Incorrect value."; // рядок str2 містить недопустимий символ
        return;
      }

    // 2. Конвертування з двійкового рядка в десяткове число
    int power = 1;
    int number = 0;
    int pos;

    for (int i=0; i<str2.length(); i++) {
      // степінь з основою 2
      if (i==0)
        power = 1;
      else
        power = power*2;

      // позиція в рядку
      pos = str2.length()-i-1;

      // додаються біти, де є символ 1
      if (str2.charAt(pos) == '1')
        number = number + power;
    }

    // 3. Конвертування десяткового числа в десятковий рядок
    str10 = "";
    while (number>0) {
      str10 = (number%10) + str10;
      number = number / 10;
    }
  }

  public String GetResult() {
    return str10;
  }
}

// Клас, що містить засоби конвертування
// з вісімкової системи числення в десяткову
class Convert_8_to_10 implements Builder {

  private String str10; // результат

  // Метод конвертування Convert() - отримує число у вигляді рядка
  // у вісімковій системі числення
  public void Convert(String str8) {

    // Потрібно конвертувати str8 в str10
    // 1. Перевірка, чи str8 коректне, якщо ні, то конвертувати в пустий рядок
    for (int i=0; i<str8.length(); i++)
      if ((str8.charAt(i)<'0') || (str8.charAt(i)>'7')) {
        str10 = "Error. Incorrect value."; // рядок str2 містить некоректний символ
        return;
      }

    // 2. Конвертування з вісімкового рядка в десяткове число
    int power = 1;
    int number = 0;
    int pos;

    for (int i=0; i<str8.length(); i++) {
      // степінь з основою 8
      if (i==0)
        power = 1;
      else
        power = power*8;

      // позиція в рядку
      pos = str8.length()-i-1;

      // формула обчислення доданку
      number = number + (str8.charAt(pos)-'0')*power;
    }

    // 3. Конвертування десяткового числа в десятковий рядок
    str10 = "";
    while (number > 0) {
      str10 = (number % 10) + str10;
      number = number / 10;
    }
  }

  // Реалізація методу GetResult()
  public String GetResult() {
    return str10;
  }
}

// Клас-розпорядник
class Director {
  // Метод, що конструює частини,
  // отримує посилання на клас, що реалізує інтерфейс з клієнтом
  void Construct(Builder builder) {
    // Побудувати продукт для клієнта
    builder.Convert("100010");
  }

  // Будує об'єкт класу для вісімкової системи числення
  void Construct2(Builder builder) {
    builder.Convert("770");
  }
}

public class TestBuilder {
  public static void main(String[] args) {
    // Функція main() виступає в ролі клієнта
    // 1. Побудова об'єкту класу Convert_2_to_10
    // 1.1. Створити конкретний екземпляр класу Convert_2_to_10
    Convert_2_to_10 obj1 = new Convert_2_to_10();

    // 1.2. Створити клас-розпорядник та зконфігурувати його продуктом obj1
    Director D = new Director();
    D.Construct(obj1);

    // 1.3. Отримати рядок результату та вивести його
    String res = obj1.GetResult();
    System.out.println("res_2 = " + res);

    // ----------------------------------------------------------------
    // 2. Побудова об'єкту класу Convert_8_to_10
    // 2.1. Створити конкретний екземпляр класу Convert_8_to_10
    Builder obj2 = new Convert_8_to_10(); // Можна і так створити екземпляр

    // 2.2. Побудувати новий екземпляр
    D.Construct2(obj2);

    // 2.3. Повернути побудований екземпляр клієнту
    String res2 = obj2.GetResult();
    System.out.println("res_8 = " + res2);
  }
}

Як видно з програмного коду, зв’язок конкретних класів-будівників (Convert_2_to_10, Convert_8_to_10) з клієнтом здійснюється через інтерфейс Builder а не через клас, як показано у попередньому прикладі. Використання інтерфейсу замість класу показано в демонстраційних цілях.

Згідно з визначенням паттерну Builder, методи в класі можуть нічого не робити. Інтерфейс або клас можуть бути замінені абстрактним класом. Все це визначається специфікою задачі.

 

3.4. Результат роботи програми

Після запуску на виконання програма видасть наступний результат

res_2 = 34
res_8 = 504

 


Теми, близькі за змістом