Patterns. Паттерн Builder. Реализация на Java. пример

Паттерн Builder. Реализация на Java. Пример

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:

 


Содержание


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




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

 


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