Шаблон проектирования “Строитель”

Шаблон “Строитель” позволяет отделить конструирование сложного объекта от его представления. Таким образом, один и тот же процесс конструирования может создавать различные представления.

Шаблон проектирования “Строитель” второго типа

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

Структура:

  • Продукт  —  сложный объект, который мы хотим создать.
  • Builder  —  абстрактный базовый класс, который определяет все шаги, необходимые для корректного создания продукта.
  • Concrete Builder реализует интерфейс Builder.

Продукт: класс Computer.

public class Computer {
private String Ram;
private String Cpu;
//геттер и сеттер
@Override
public String toString() {
return "Computer{" +
"Ram='" + Ram + '\'' +
", Cpu='" + Cpu + '\'' +
'}';
}
}

Класс Builder:

public abstract class Builder {
protected Computer computer = new Computer();
public abstract void buildRam();
public abstract void buildCpu();
public abstract Computer createComputer();
}

Конкретные классы Builder: AsusComputerBuilder и AcerComputerBuilder.

public class AcerComputerBuilder extends Builder{
@Override
public void buildRam() {
computer.setRam("(Acer) 8GB RAM");
}
@Override
public void buildCpu() {
computer.setCpu("(Acer) AMD CPU");
}
@Override
public Computer createComputer() {
return computer;
}
}
public class AsusComputerBuilder extends Builder{
@Override
public void buildRam() {
computer.setRam("(Asus) 16GB RAM");
}
@Override
public void buildCpu() {
computer.setCpu("(Asus) Intel CPU");
}
@Override
public Computer createComputer() {
return computer;
}
}

Класс Director:

public class Director {
private Builder builder;
public Director(Builder builder){
this.builder = builder;
}
public Computer construct(){
builder.buildRam();
builder.buildCpu();
return builder.computer;
}
}

Применение:

public class Client {
public static void main(String[] args) {
Director director = new Director(new AsusComputerBuilder());
Computer computer = director.construct();
System.out.println(computer);//toString()
}
}
Output:
Computer{Ram='(Asus) 16GB RAM', Cpu='(Asus) Intel CPU'}

В данном случае абстрактный класс Computer определил, какой компонент нужен, а абстрактный класс Builder выявил метод для сборки компонента компьютера. После этого мы можем создавать различные типы компьютера в конкретном классе Builder, например AsusComputerBuilder и AcerComputerBuilder, и реализовывать метод, определенный абстрактным классом Builder.

Для создания полноценного компьютера с помощью “Строителя” понадобится класс Director, для чего схема применения направляется в конкретный Builder, который мы объявили в конструкторе Director.

Шаблон проектирования “Строитель” второго типа

Когда конструктор класса требует много параметров, читаемость класса ухудшается, и в нем легко допустить опечатки. В этом случае для рефакторинга можно использовать шаблон “Строитель”.

До:

public class Computer {
private String cpu;
private String ram;
private String storage;
private String brand;
public Computer(String cpu, String ram, String storage, String brand) {
this.cpu = cpu;
this.ram = ram;
this.storage = storage;
this.brand = brand;
}
// геттер и сеттер
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", storage='" + storage + '\'' +
", brand='" + brand + '\'' +
'}';
}
}

Применение:

public static void main(String[] args) {
Computer computer = new Computer("Intel","Kingston 8GB","WD SSD 500GB","Asus");
System.out.println(computer);
//Вывод: Phone{cpu='Intel', ram='Kingston 8GB', storage='WD SSD 500GB', brand='Asus'}
}

После:

public class Computer {
private String cpu;
private String ram;
private String storage;
private String brand;

private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.brand = builder.brand;
}

public static final class Builder{
private String cpu;
private String ram;
private String storage;
private String brand;
public Builder cpu(String cpu){
this.cpu = cpu;
return this;//возврат Builder
}
public Builder ram(String ram){
this.ram = ram;
return this;
}
public Builder storage(String storage){
this.storage = storage;
return this;
}
public Builder brand(String brand){
this.brand = brand;
return this;
}
public Computer build(){
return new Computer(this);
}
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", storage='" + storage + '\'' +
", brand='" + brand + '\'' +
'}';
}
}

Применение: 

public static void main(String[] args) {
Computer computer = new Computer.Builder() //создание класса Builder для вызова метода builder
.cpu("Intel") //установка данных cpu и возврат класса Builder с помощью ключевого слова "this"
.ram("Kingston 8GB")//ram можно вызвать после вызова метода cpu(), поскольку метод cpu() возвращает ключевое слово "this" класса Builder.
.storage("WD SSD 500GB")
.brand("Asus")
.build();//вызов метода build() для вызова частного конструктора Computer с целью создания объекта Computer
System.out.println(computer);//Computer{cpu='Intel', ram='Kingston 8GB', storage='WD SSD 500GB', brand='Asus'}
}

В данном случае мы модифицируем конструктор Computer в модификатор с частным доступом. Затем, используя внутренний класс Builder, собираем каждый компонент и вызываем метод build() для создания объекта Computer.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Lim Yee Jie: Understanding Design Pattern — Builder Pattern

Предыдущая статьяПоток управления декларативным циклом в Angular 17
Следующая статьяСекрет производительности Kafka