Builder что это
Перейти к содержимому

Builder что это

  • автор:

Builder что это

Строитель (Builder) — шаблон проектирования, который инкапсулирует создание объекта и позволяет разделить его на различные этапы.

Когда использовать паттерн Строитель?

  • Когда процесс создания нового объекта не должен зависеть от того, из каких частей этот объект состоит и как эти части связаны между собой
  • Когда необходимо обеспечить получение различных вариаций объекта в процессе его создания

Формально в UML паттерн мог бы выглядеть следующим образом:

Паттерн Строитель в C# и .NET

Формальное определение на C# могло бы выглядеть так:

class Client < void Main() < Builder builder = new ConcreteBuilder(); Director director = new Director(builder); director.Construct(); Product product = builder.GetResult(); >> class Director < Builder builder; public Director(Builder builder) < this.builder = builder; >public void Construct() < builder.BuildPartA(); builder.BuildPartB(); builder.BuildPartC(); >> abstract class Builder < public abstract void BuildPartA(); public abstract void BuildPartB(); public abstract void BuildPartC(); public abstract Product GetResult(); >class Product < Listparts = new List(); public void Add(string part) < parts.Add(part); >> class ConcreteBuilder : Builder < Product product = new Product(); public override void BuildPartA() < product.Add("Part A"); >public override void BuildPartB() < product.Add("Part B"); >public override void BuildPartC() < product.Add("Part C"); >public override Product GetResult() < return product; >>

Участники

  • Product : представляет объект, который должен быть создан. В данном случае все части объекта заключены в списке parts.
  • Builder : определяет интерфейс для создания различных частей объекта Product
  • ConcreteBuilder : конкретная реализация Buildera. Создает объект Product и определяет интерфейс для доступа к нему
  • Director : распорядитель — создает объект, используя объекты Builder

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

//мука class Flour < // какого сорта мука public string Sort < get; set; >> // соль class Salt <> // пищевые добавки class Additives < public string Name < get; set; >> class Bread < // мука public Flour Flour < get; set; >// соль public Salt Salt < get; set; >// пищевые добавки public Additives Additives < get; set; >public override string ToString() < StringBuilder sb = new StringBuilder(); if (Flour != null) sb.Append(Flour.Sort + "\n"); if (Salt != null) sb.Append("Соль \n"); if (Additives != null) sb.Append("Добавки: "+Additives.Name+" \n"); return sb.ToString(); >>

Кстати в данном случае для построения строки используется класс StringBuilder.

Хлеб может иметь различную комбинацию компонентов: ржаной и пшеничной муки, соли, пищевых добавок. И нам надо обеспечить выпечку разных сортов хлеба. Для разных сортов хлеба может варьироваться конкретный набор компонентов, не все компоненты могут использоваться. И для этой задачи применим паттерн Builder:

class Program < static void Main(string[] args) < // содаем объект пекаря Baker baker = new Baker(); // создаем билдер для ржаного хлеба BreadBuilder builder = new RyeBreadBuilder(); // выпекаем Bread ryeBread = baker.Bake(builder); Console.WriteLine(ryeBread.ToString()); // оздаем билдер для пшеничного хлеба builder = new WheatBreadBuilder(); Bread wheatBread = baker.Bake(builder); Console.WriteLine(wheatBread.ToString()); Console.Read(); >> // абстрактный класс строителя abstract class BreadBuilder < public Bread Bread < get; private set; >public void CreateBread() < Bread = new Bread(); >public abstract void SetFlour(); public abstract void SetSalt(); public abstract void SetAdditives(); > // пекарь class Baker < public Bread Bake(BreadBuilder breadBuilder) < breadBuilder.CreateBread(); breadBuilder.SetFlour(); breadBuilder.SetSalt(); breadBuilder.SetAdditives(); return breadBuilder.Bread; >> // строитель для ржаного хлеба class RyeBreadBuilder : BreadBuilder < public override void SetFlour() < this.Bread.Flour = new Flour < Sort = "Ржаная мука 1 сорт" >; > public override void SetSalt() < this.Bread.Salt = new Salt(); >public override void SetAdditives() < // не используется >> // строитель для пшеничного хлеба class WheatBreadBuilder : BreadBuilder < public override void SetFlour() < this.Bread.Flour = new Flour < Sort = "Пшеничная мука высший сорт" >; > public override void SetSalt() < this.Bread.Salt = new Salt(); >public override void SetAdditives() < this.Bread.Additives = new Additives < Name = "улучшитель хлебопекарный" >; > >

Консольный вывод программы:

Ржаная мука 1 сорт Соль Пшеничная мука высший сорт Соль Добавки: улучшитель хлебопекарный

В данном случае с помощью конкретных строителей RyeBreadBuilder и WheatBreadBuilder создаются объекты Bread с определенным набором. В роли распорядителя выступает класс пекаря Baker, который вызывает методы конкретных строителей для построения нового объекта.

Вы отправили слишком много запросов, поэтому ваш компьютер был заблокирован.

Для того, чтобы предотвратить автоматическое считывание информации с нашего сервиса, на Linguee допустимо лишь ограниченное количество запросов на каждого пользователя.
Пользователям, браузер которых поддерживает Javascript, доступно большее количество запросов, в отличие от пользователей, чей браузер не поддерживает Javascript. Попробуйте активировать Javascript в настройках вашего браузера, подождать несколько часов и снова воспользоваться нашим сервером.
Если же ваш компьютер является частью сети компьютеров, в которой большое количество пользователей одновременно пользуется Linguee,сообщитеоб этом нам.

Паттерн проектирования Builder (Строитель) в Java

А вот и я со своей очередной статьей о паттернах проектирования, а именно о паттерне проектирования Builder (он же Строитель). Очень полезный паттерн проектирования, который позволяет нам шаг за шагом конструировать сложные объекты.

Паттерн проектирования Builder

  • Паттерн проектирования Builder разработан для обеспечения гибкого решения различных задач создания объектов в объектно-ориентированном программировании.
  • Паттерн проектирования Builder позволяет отделить построение сложного объекта от его представления.
  • Паттерн Builder создает сложные объекты, используя простые объекты и поэтапный подход.
  • Паттерн предоставляет один из лучших способов создания сложных объектов.
  • Это один из паттернов проектирования банды четырех (GoF), которые описывают, как решать периодически возникающие задачи проектирования в объектно-ориентированном программном обеспечении.
  • Этот паттерн полезен для создания разных иммутабельных объектов с помощью одного и того же процесса построения объекта.

Паттерн Builder — это паттерн проектирования, который позволяет поэтапно создавать сложные объекты с помощью четко определенной последовательности действий. Строительство контролируется объектом-распорядителем (director), которому нужно знать только тип создаваемого объекта.

Итак, паттерн проектирования Builder можно разбить на следующие важные компоненты:

  • Product (продукт) — Класс, который определяет сложный объект, который мы пытаемся шаг за шагом сконструировать, используя простые объекты.
  • Builder (строитель) — абстрактный класс/интерфейс, который определяет все этапы, необходимые для производства сложного объекта-продукта. Как правило, здесь объявляются (абстрактно) все этапы (buildPart), а их реализация относится к классам конкретных строителей (ConcreteBuilder).
  • ConcreteBuilder (конкретный строитель) — класс-строитель, который предоставляет фактический код для создания объекта-продукта. У нас может быть несколько разных ConcreteBuilder-классов, каждый из которых реализует различную разновидность или способ создания объекта-продукта.
  • Director (распорядитель) — супервизионный класс, под конролем котрого строитель выполняет скоординированные этапы для создания объекта-продукта. Распорядитель обычно получает на вход строителя с этапами на выполнение в четком порядке для построения объекта-продукта.

Паттерн проектирования Builder решает такие проблемы, как:

  • Как класс (тот же самый процесс строительства) может создавать различные представления сложного объекта?
  • Как можно упростить класс, занимающийся созданием сложного объекта?

Давайте реализуем пример со сборкой автомобилей, используя паттерн проектирования Builder.

Пример со сборкой автомобилей с использованием паттерна проектирования Builder

Шаг 1: Создайте класс Car (автомобиль), который в нашем примере является продуктом:

package org.trishinfotech.builder; public class Car < private String chassis; private String body; private String paint; private String interior; public Car() < super(); >public Car(String chassis, String body, String paint, String interior) < this(); this.chassis = chassis; this.body = body; this.paint = paint; this.interior = interior; >public String getChassis() < return chassis; >public void setChassis(String chassis) < this.chassis = chassis; >public String getBody() < return body; >public void setBody(String body) < this.body = body; >public String getPaint() < return paint; >public void setPaint(String paint) < this.paint = paint; >public String getInterior() < return interior; >public void setInterior(String interior) < this.interior = interior; >public boolean doQualityCheck() < return (chassis != null && !chassis.trim().isEmpty()) && (body != null && !body.trim().isEmpty()) && (paint != null && !paint.trim().isEmpty()) && (interior != null && !interior.trim().isEmpty()); >@Override public String toString() < // StringBuilder class also uses Builder Design Pattern with implementation of java.lang.Appendable interface StringBuilder builder = new StringBuilder(); builder.append("Car [chassis=").append(chassis).append(", body=").append(body).append(", paint java">package org.trishinfotech.builder; public interface CarBuilder < // Этап 1 public CarBuilder fixChassis(); // Этап 2 public CarBuilder fixBody(); // Этап 3 public CarBuilder paint(); // Этап 4 public CarBuilder fixInterior(); // Выпуск автомобиля public Car build(); >

Обратите внимание, что я сделал тип CarBuilder типом возврата всех этапов, созданных здесь. Это позволит нам вызывать этапы по цепочке. Здесь есть один очень важный метод build , который заключается в том, чтобы получить результат или создать конечный объект Car . Этот метод фактически проверяет годность автомобиля и выпускает (возвращает) его только в том случае, если его сборка завершена успешно (все валидно).

Шаг 3: Теперь пора написать ConcreteBuilder . Как я уже упоминал, у нас могут быть разные варианты ConcreteBuilder , и каждый из них выполняет сборку по-своему, чтобы предоставить нам различные представления сложного объекта Car .

Итак, ниже приведен код ClassicCarBuilder , который собирает старые модели автомобилей.

package org.trishinfotech.builder; public class ClassicCarBuilder implements CarBuilder < private String chassis; private String body; private String paint; private String interior; public ClassicCarBuilder() < super(); >@Override public CarBuilder fixChassis() < System.out.println("Assembling chassis of the classical model"); this.chassis = "Classic Chassis"; return this; >@Override public CarBuilder fixBody() < System.out.println("Assembling body of the classical model"); this.body = "Classic Body"; return this; >@Override public CarBuilder paint() < System.out.println("Painting body of the classical model"); this.paint = "Classic White Paint"; return this; >@Override public CarBuilder fixInterior() < System.out.println("Setting up interior of the classical model"); this.interior = "Classic interior"; return this; >@Override public Car build() < Car car = new Car(chassis, body, paint, interior); if (car.doQualityCheck()) < return car; >else < System.out.println("Car assembly is incomplete. Can't deliver!"); >return null; > >

Теперь напишем еще один строитель ModernCarBuilder для сборки последней модели автомобиля.

package org.trishinfotech.builder; public class ModernCarBuilder implements CarBuilder < private String chassis; private String body; private String paint; private String interior; public ModernCarBuilder() < super(); >@Override public CarBuilder fixChassis() < System.out.println("Assembling chassis of the modern model"); this.chassis = "Modern Chassis"; return this; >@Override public CarBuilder fixBody() < System.out.println("Assembling body of the modern model"); this.body = "Modern Body"; return this; >@Override public CarBuilder paint() < System.out.println("Painting body of the modern model"); this.paint = "Modern Black Paint"; return this; >@Override public CarBuilder fixInterior() < System.out.println("Setting up interior of the modern model"); this.interior = "Modern interior"; return this; >@Override public Car build() < Car car = new Car(chassis, body, paint, interior); if (car.doQualityCheck()) < return car; >else < System.out.println("Car assembly is incomplete. Can't deliver!"); >return null; > >

И еще один SportsCarBuilder для создания спортивного автомобиля.

package org.trishinfotech.builder; public class SportsCarBuilder implements CarBuilder < private String chassis; private String body; private String paint; private String interior; public SportsCarBuilder() < super(); >@Override public CarBuilder fixChassis() < System.out.println("Assembling chassis of the sports model"); this.chassis = "Sporty Chassis"; return this; >@Override public CarBuilder fixBody() < System.out.println("Assembling body of the sports model"); this.body = "Sporty Body"; return this; >@Override public CarBuilder paint() < System.out.println("Painting body of the sports model"); this.paint = "Sporty Torch Red Paint"; return this; >@Override public CarBuilder fixInterior() < System.out.println("Setting up interior of the sports model"); this.interior = "Sporty interior"; return this; >@Override public Car build() < Car car = new Car(chassis, body, paint, interior); if (car.doQualityCheck()) < return car; >else < System.out.println("Car assembly is incomplete. Can't deliver!"); >return null; > >

Шаг 4: Теперь мы напишем класс-распорядитель AutomotiveEngineer , под руководством которого строитель будет собирать автомобиль (объект Car ) шаг за шагом в четко определенном порядке.

package org.trishinfotech.builder; public class AutomotiveEngineer < private CarBuilder builder; public AutomotiveEngineer(CarBuilder builder) < super(); this.builder = builder; if (this.builder == null) < throw new IllegalArgumentException("Automotive Engineer can't work without Car Builder!"); >> public Car manufactureCar() < return builder.fixChassis().fixBody().paint().fixInterior().build(); >>

Мы видим, что метод manufactureCar вызывает этапы сборки автомобиля в правильном порядке.

Теперь пришло время написать класс Main для выполнения и тестирования нашего кода.

package org.trishinfotech.builder; public class Main < public static void main(String[] args) < CarBuilder builder = new SportsCarBuilder(); AutomotiveEngineer engineer = new AutomotiveEngineer(builder); Car car = engineer.manufactureCar(); if (car != null) < System.out.println("Below car delievered: "); System.out.println("======================================================================"); System.out.println(car); System.out.println("===================================================================== java">Assembling chassis of the sports model Assembling body of the sports model Painting body of the sports model Setting up interior of the sports model Below car delievered: ====================================================================== Car [chassis=Sporty Chassis, body=Sporty Body, paint=Sporty Torch Red Paint, interior=Sporty interior] ======================================================================

Я надеюсь, что вы хорошо разобрались в объяснении и примере, чтобы понять паттерн Builder . Некоторые из нас также находят у него сходство с паттерном абстрактной фабрики (Abstract Factory), о котором я рассказывал в другой статье. Основное различие между строителем и абстрактной фабрикой состоит в том, что строитель предоставляет нам больший или лучший контроль над процессом создания объекта. Если вкратце, то паттерн абстрактной фабрики отвечает на вопрос «что», а паттерн строитель — «как».

Я нашел паттерн Builder невероятно полезным и одним из наиболее часто используемых в приложениях в настоящее время. Я пришел к выводу, что Builder лучше подходит для работы с иммутабельными объектами. Все мы знаем, как много есть хороших иммутабельных объектов, и их использование увеличивается день ото дня, особенно после релиза Java 8.

Я использую Builder для написания своих сложных иммутабельных классов, и я бы хотел продемонстриовать здесь эту идею.

В качестве примера у нас есть класс Employee , в котором есть несколько полей.

public class Employee

Предположим, только два поля EmpNo и EmpName являются обязательными, а все остальные — опциональные. Поскольку это иммутабельный класс, у меня есть два варианта написания конструкторов.

  1. Написать конструктор с параметрами под все поля.
  2. Написать несколько конструкторов для разных комбинаций параметров, чтобы создать разные представления объекта Employee .

Я решил, что первый вариант мне не подходит, так как мне не нравится, когда в методе больше трех-четырех параметров. Это выглядит не очень хорошо и становится еще хуже, когда многие параметры равны нулю или null .

Employee emp1 = new Employee (100, "Brijesh", null, 0, 0, "Builder Pattern");

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

 public Employee(int empNo, String name) < super(); if (empNo if (name == null || name.trim().isEmpty()) < throw new IllegalArgumentException("Please provide employee name."); >this.empNo = empNo; this.name = name; > public Employee(int empNo, String name, String depttName) < this(empNo, name); this.depttName = depttName; >public Employee(int empNo, String name, String depttName, int salary) < this(empNo, name, depttName); this.salary = salary; >public Employee(int empNo, String name, String depttName, int salary, int mgrEmpNo) < this(empNo, name, depttName, salary); this.mgrEmpNo = mgrEmpNo; >public Employee(int empNo, String name, String depttName, int salary, int mgrEmpNo, String projectName)

Итак, вот решение с помощью паттерна Builder :

package org.trishinfotech.builder.example; public class Employee < private int empNo; private String name; private String depttName; private int salary; private int mgrEmpNo; private String projectName; public Employee(EmployeeBuilder employeeBuilder) < if (employeeBuilder == null) < throw new IllegalArgumentException("Please provide employee builder to build employee object."); >if (employeeBuilder.empNo if (employeeBuilder.name == null || employeeBuilder.name.trim().isEmpty()) < throw new IllegalArgumentException("Please provide employee name."); >this.empNo = employeeBuilder.empNo; this.name = employeeBuilder.name; this.depttName = employeeBuilder.depttName; this.salary = employeeBuilder.salary; this.mgrEmpNo = employeeBuilder.mgrEmpNo; this.projectName = employeeBuilder.projectName; > public int getEmpNo() < return empNo; >public String getName() < return name; >public String getDepttName() < return depttName; >public int getSalary() < return salary; >public int getMgrEmpNo() < return mgrEmpNo; >public String getProjectName() < return projectName; >@Override public String toString() < // Класс StringBuilder также использует паттерн проектирования Builder с реализацией // интерфейса java.lang.Appendable StringBuilder builder = new StringBuilder(); builder.append("Employee [empNo=").append(empNo).append(", name=").append(name).append(", depttName=") .append(depttName).append(", salary=").append(salary).append(", mgrEmpNo=").append(mgrEmpNo) .append(", projectName=").append(projectName).append("]"); return builder.toString(); >public static class EmployeeBuilder < private int empNo; protected String name; protected String depttName; protected int salary; protected int mgrEmpNo; protected String projectName; public EmployeeBuilder() < super(); >public EmployeeBuilder empNo(int empNo) < this.empNo = empNo; return this; >public EmployeeBuilder name(String name) < this.name = name; return this; >public EmployeeBuilder depttName(String depttName) < this.depttName = depttName; return this; >public EmployeeBuilder salary(int salary) < this.salary = salary; return this; >public EmployeeBuilder mgrEmpNo(int mgrEmpNo) < this.mgrEmpNo = mgrEmpNo; return this; >public EmployeeBuilder projectName(String projectName) < this.projectName = projectName; return this; >public Employee build() < Employee emp = null; if (validateEmployee()) < emp = new Employee(this); >else < System.out.println("Sorry! Employee objects can't be build without required details"); >return emp; > private boolean validateEmployee() < return (empNo >0 && name != null && !name.trim().isEmpty()); > > >

Я написал EmployeeBuilder как публичный статический вложенный класс. Вы можете написать его как обычный публичный класс в отдельном файл Java. Большой разницы я не вижу.

Теперь напишем программу EmployeeMain для создания объекта Employee :

package org.trishinfotech.builder.example; public class EmployeeMain < public static void main(String[] args) < Employee emp1 = new Employee.EmployeeBuilder().empNo(100).name("Brijesh").projectName("Builder Pattern") .build(); System.out.println(emp1); >>

Надеюсь, вам понравилась идея. Мы можем использовать это при создании более сложных объектов. Я не реализовал здесь распорядителя (Director), так как все шаги (сбор значений для полей) не являются обязательными и могут выполняться в любом порядке. Чтобы убедиться, что я создаю объект Employee только после получения всех обязательных полей, я написал метод проверки.

Пример с оформлением заказа в ресторане с использованием паттерна Builder

Я хочу еще показать вам пример кода для оформления заказа в ресторане, где Order (заказ) является иммутабельным объектом и требует тип обслуживания заказа — Order Service Type (Take Away — с собой/Eat Here — в заведении), всех необходимых нам продуктов питания (Food Items) и имени клиента (Customer Name — опционально) в время оформления заказа. Продуктов питания может быть сколько угодно. Итак, вот код этого примера.

Код для перечисления OrderService :

package org.trishinfotech.builder; public enum OrderService < TAKE_AWAY("Take Away", 2.0d), EAT_HERE("Eat Here", 5.5d); private String name; private double tax; OrderService(String name, double tax) < this.name = name; this.tax = tax; >public String getName() < return name; >public double getTax() < return tax; >>

Код для интерфейса FoodItem :

package org.trishinfotech.builder.meal; import org.trishinfotech.builder.packing.Packing; public interface FoodItem

Код для класса Meal (блюдо). Класс Meal предлагает заранее определенные продукты питания со скидкой на цену товара (не на цену упаковки).

package org.trishinfotech.builder.meal; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.trishinfotech.builder.packing.MultiPack; import org.trishinfotech.builder.packing.Packing; public class Meal implements FoodItem < private ListfoodItems = new ArrayList(); private String mealName; private double discount; public Meal(String mealName, List foodItems, double discount) < super(); if (Objects.isNull(foodItems) || foodItems.stream().filter(Objects::nonNull).collect(Collectors.toList()).isEmpty()) < throw new IllegalArgumentException( "Meal can't be order without any food item"); >this.mealName = mealName; this.foodItems = new ArrayList(foodItems); this.discount = discount; > public List getFoodItems() < return foodItems; >@Override public String name() < return mealName; >@Override public int calories() < int totalCalories = foodItems.stream().mapToInt(foodItem ->foodItem.calories()).sum(); return totalCalories; > @Override public Packing packing() < double packingPrice = foodItems.stream().map(foodItem ->foodItem.packing()) .mapToDouble(packing -> packing.packingPrice()).sum(); return new MultiPack(packingPrice); > @Override public double price() < double totalPrice = foodItems.stream().mapToDouble(foodItem ->foodItem.price()).sum(); return totalPrice; > public double discount() < return discount; >>

Еда:

Код для класса Burger :

package org.trishinfotech.builder.food.burger; import org.trishinfotech.builder.meal.FoodItem; import org.trishinfotech.builder.packing.Packing; import org.trishinfotech.builder.packing.Wrap; public abstract class Burger implements FoodItem < @Override public Packing packing() < return new Wrap(); >>

Код для класса ChickenBurger :

package org.trishinfotech.builder.food.burger; public class ChickenBurger extends Burger < @Override public String name() < return "Chicken Burger"; >@Override public int calories() < return 300; >@Override public double price() < return 4.5d; >>

Код для класса VegBurger (веганский бургер):

package org.trishinfotech.builder.food.burger; public class VegBurger extends Burger < @Override public String name() < return "Veg Burger"; >@Override public int calories() < return 180; >@Override public double price() < return 2.7d; >>

Код для класса Nuggets :

package org.trishinfotech.builder.food.nuggets; import org.trishinfotech.builder.meal.FoodItem; import org.trishinfotech.builder.packing.Container; import org.trishinfotech.builder.packing.Packing; public abstract class Nuggets implements FoodItem < @Override public Packing packing() < return new Container(); >>

Код для класса CheeseNuggets :

package org.trishinfotech.builder.food.nuggets; public class CheeseNuggets extends Nuggets < @Override public String name() < return "Cheese Nuggets"; >@Override public int calories() < return 330; >@Override public double price() < return 3.8d; >>

Код для класса ChickenNuggets :

package org.trishinfotech.builder.food.nuggets; public class ChickenNuggets extends Nuggets < @Override public String name() < return "Chicken Nuggets"; >@Override public int calories() < return 450; >@Override public double price() < return 5.0d; >>

Напитки:

Напитки бывают разных размеров. Итак, вот код перечисления BeverageSize :

package org.trishinfotech.builder.beverages; public enum BeverageSize < XS("Extra Small", 110), S("Small", 150), M("Medium", 210), L("Large", 290); private String name; private int calories; BeverageSize(String name, int calories) < this.name = name; this.calories = calories; >public String getName() < return name; >public int getCalories() < return calories; >>

Код для класса Drink :

package org.trishinfotech.builder.beverages; import org.trishinfotech.builder.meal.FoodItem; public abstract class Drink implements FoodItem < protected BeverageSize size; public Drink(BeverageSize size) < super(); this.size = size; if (this.size == null) < this.size = BeverageSize.M; >> public BeverageSize getSize() < return size; >public String drinkDetails() < return " (" + size + ")"; >>

Код для класса ColdDrink :

package org.trishinfotech.builder.beverages.cold; import org.trishinfotech.builder.beverages.BeverageSize; import org.trishinfotech.builder.beverages.Drink; import org.trishinfotech.builder.packing.Bottle; import org.trishinfotech.builder.packing.Packing; public abstract class ColdDrink extends Drink < public ColdDrink(BeverageSize size) < super(size); >@Override public Packing packing() < return new Bottle(); >>

Код для класса CocaCola :

package org.trishinfotech.builder.beverages.cold; import org.trishinfotech.builder.beverages.BeverageSize; public class CocaCola extends ColdDrink < public CocaCola(BeverageSize size) < super(size); >@Override public String name() < return "Coca-Cola" + drinkDetails(); >@Override public int calories() < if (size != null) < switch (size) < case XS: return 110; case S: return 150; case M: return 210; case L: return 290; default: break; >> return 0; > @Override public double price() < if (size != null) < switch (size) < case XS: return 0.80d; case S: return 1.0d; case M: return 1.5d; case L: return 2.0d; default: break; >> return 0.0d; > >

Код для класса Pepsi :

package org.trishinfotech.builder.beverages.cold; import org.trishinfotech.builder.beverages.BeverageSize; public class Pepsi extends ColdDrink < public Pepsi(BeverageSize size) < super(size); >@Override public String name() < return "Pepsi" + drinkDetails(); >@Override public int calories() < if (size != null) < switch (size) < case S: return 160; case M: return 220; case L: return 300; default: break; >> return 0; > @Override public double price() < if (size != null) < switch (size) < case S: return 1.2d; case M: return 2.2d; case L: return 2.7d; default: break; >> return 0.0d; > >

Код для класса HotDrink :

package org.trishinfotech.builder.beverages.hot; import org.trishinfotech.builder.beverages.BeverageSize; import org.trishinfotech.builder.beverages.Drink; import org.trishinfotech.builder.packing.Packing; import org.trishinfotech.builder.packing.SipperMug; public abstract class HotDrink extends Drink < public HotDrink(BeverageSize size) < super(size); >@Override public Packing packing() < return new SipperMug(); >>

Код для класса Cuppuccinno :

package org.trishinfotech.builder.beverages.hot; import org.trishinfotech.builder.beverages.BeverageSize; public class Cappuccino extends HotDrink < public Cappuccino(BeverageSize size) < super(size); >@Override public String name() < return "Cappuccino" + drinkDetails(); >@Override public int calories() < if (size != null) < switch (size) < case S: return 120; case M: return 160; case L: return 210; default: break; >> return 0; > @Override public double price() < if (size != null) < switch (size) < case S: return 1.0d; case M: return 1.4d; case L: return 1.8d; default: break; >> return 0.0d; > >

Код для класса HotChocolate :

package org.trishinfotech.builder.beverages.hot; import org.trishinfotech.builder.beverages.BeverageSize; public class HotChocolate extends HotDrink < public HotChocolate(BeverageSize size) < super(size); >@Override public String name() < return "Hot Chocolate" + drinkDetails(); >@Override public int calories() < if (size != null) < switch (size) < case S: return 370; case M: return 450; case L: return 560; default: break; >> return 0; > @Override public double price() < if (size != null) < switch (size) < case S: return 1.6d; case M: return 2.3d; case L: return 3.0d; default: break; >> return 0.0d; > >

Упаковка:

Код интерфейса Packing :

package org.trishinfotech.builder.packing; public interface Packing

Код для класса Bottle :

package org.trishinfotech.builder.packing; public class Bottle implements Packing < @Override public String pack() < return "Bottle"; >@Override public double packingPrice() < return 0.75d; >>

Код для класса Container :

package org.trishinfotech.builder.packing; public class Container implements Packing < @Override public String pack() < return "Container"; >@Override public double packingPrice() < return 1.25d; >>

Код для класса MultiPack . Упаковка MutiPack служит вспомогательной упаковкой для еды, когда мы используем разные упаковки для разных продуктов.

package org.trishinfotech.builder.packing; public class MultiPack implements Packing < private double packingPrice; public MultiPack(double packingPrice) < super(); this.packingPrice = packingPrice; >@Override public String pack() < return "Multi-Pack"; >@Override public double packingPrice() < return packingPrice; >>

Код для класса SipperMug :

package org.trishinfotech.builder.packing; public class SipperMug implements Packing < @Override public String pack() < return "Sipper Mug"; >@Override public double packingPrice() < return 1.6d; >>

Код для класса Wrap :

package org.trishinfotech.builder.packing; public class Wrap implements Packing < @Override public String pack() < return "Wrap"; >@Override public double packingPrice() < return 0.40d; >>

Код служебного класса BillPrinter , который я написал для печати детализированного счета.

package org.trishinfotech.builder.util; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.atomic.DoubleAdder; import org.trishinfotech.builder.Order; import org.trishinfotech.builder.OrderService; import org.trishinfotech.builder.meal.Meal; import org.trishinfotech.builder.packing.Packing; public class BillPrinter < static DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); public static void printItemisedBill(Order order) < OrderService service = order.getService(); System.out.printf("%60s\n", "Food Court"); System.out.println("================================================================================================================="); System.out.printf("Service: %10s (%2.2f Tax) Customer Name: %-20s\n", service.getName(), service.getTax(), order.getCustomerName()); System.out.println("-----------------------------------------------------------------------------------------------------------------"); System.out.printf("%25s | %10s | %10s | %10s | %15s | %10s | %10s\n", "Food Item", "Calories", "Packing", "Price", "Packing Price", "Discount %", "Total Price"); System.out.println("-----------------------------------------------------------------------------------------------------------------"); DoubleAdder itemTotalPrice = new DoubleAdder(); order.getFoodItems().stream().forEach(item ->< String name = item.name(); int calories = item.calories(); Packing packing = item.packing(); double price = item.price(); double packingPrice = packing.packingPrice(); double discount = item instanceof Meal? ((Meal)item).discount() : 0.0d; double totalItemPrice = calculateTotalItemPrice(price, packingPrice, discount); System.out.printf("%25s | %10d | %10s | %10.2f | %15.2f | %10.2f | %10.2f\n", name, calories, packing.pack(), price, packing.packingPrice(), discount, totalItemPrice); itemTotalPrice.add(totalItemPrice); >); System.out.println("================================================================================================================="); double billTotal = itemTotalPrice.doubleValue(); billTotal = applyTaxes(billTotal, service); System.out.printf("Date: %-30s %66s %.2f\n", dtf.format(LocalDateTime.now()), "Total Bill (incl. taxes):", billTotal); System.out.println("Enjoy your meal!\n\n\n\n"); > private static double applyTaxes(double billTotal, OrderService service) < return billTotal + (billTotal * service.getTax())/100; >private static double calculateTotalItemPrice(double price, double packingPrice, double discount) < if (discount >0.0d) < price = price - (price * discount)/100; >return price + packingPrice; > >

Почти все готово. Пришло время написать наш иммутабельный класс Order :

package org.trishinfotech.builder; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.trishinfotech.builder.meal.FoodItem; public class Order < private ListfoodItems = new ArrayList(); private String customerName; private OrderService service; public Order(OrderService service, List foodItems, String customerName) < super(); if (Objects.isNull(service)) < throw new IllegalArgumentException( "Meal can't be order without selecting service 'Take Away' or 'Eat Here'"); >if (Objects.isNull(foodItems) || foodItems.stream().filter(Objects::nonNull).collect(Collectors.toList()).isEmpty()) < throw new IllegalArgumentException( "Meal can't be order without any food item"); >this.service = service; this.foodItems = new ArrayList(foodItems); this.customerName = customerName; if (this.customerName == null) < this.customerName = "NO NAME"; >> public List getFoodItems() < return foodItems; >public String getCustomerName() < return customerName; >public OrderService getService() < return service; >>

А вот код для OrderBuilder , который конструирует объект Order .

package org.trishinfotech.builder; import java.util.ArrayList; import java.util.List; import org.trishinfotech.builder.beverages.BeverageSize; import org.trishinfotech.builder.beverages.cold.CocaCola; import org.trishinfotech.builder.beverages.cold.Pepsi; import org.trishinfotech.builder.food.burger.ChickenBurger; import org.trishinfotech.builder.food.burger.VegBurger; import org.trishinfotech.builder.food.nuggets.CheeseNuggets; import org.trishinfotech.builder.food.nuggets.ChickenNuggets; import org.trishinfotech.builder.meal.FoodItem; import org.trishinfotech.builder.meal.Meal; public class OrderBuilder < protected static final double HAPPY_MENU_DISCOUNT = 5.0d; private String customerName; private OrderService service = OrderService.TAKE_AWAY; private Listitems = new ArrayList(); public OrderBuilder() < super(); >// Сеттеры для каждого поля в целевом объекте. В этом примере это Order. // Возвращаемым типом у нас будет сам Builder (например, OrderBuilder), чтобы сделать возможным цепной вызов сеттеров. public OrderBuilder name(String customerName) < this.customerName = customerName; return this; >public OrderBuilder service(OrderService service) < if (service != null) < this.service = service; >return this; > public OrderBuilder item(FoodItem item) < items.add(item); return this; >// Комбо предложения public OrderBuilder vegNuggetsHappyMeal() < ListfoodItems = new ArrayList(); foodItems.add(new CheeseNuggets()); foodItems.add(new Pepsi(BeverageSize.S)); Meal meal = new Meal("Veg Nuggets Happy Meal", foodItems, HAPPY_MENU_DISCOUNT); return item(meal); > public OrderBuilder chickenNuggetsHappyMeal() < ListfoodItems = new ArrayList(); foodItems.add(new ChickenNuggets()); foodItems.add(new CocaCola(BeverageSize.S)); Meal meal = new Meal("Chicken Nuggets Happy Meal", foodItems, HAPPY_MENU_DISCOUNT); return item(meal); > public OrderBuilder vegBurgerHappyMeal() < ListfoodItems = new ArrayList(); foodItems.add(new VegBurger()); foodItems.add(new Pepsi(BeverageSize.S)); Meal meal = new Meal("Veg Burger Happy Meal", foodItems, HAPPY_MENU_DISCOUNT); return item(meal); > public OrderBuilder chickenBurgerHappyMeal() < ListfoodItems = new ArrayList(); foodItems.add(new ChickenBurger()); foodItems.add(new CocaCola(BeverageSize.S)); Meal meal = new Meal("Chicken Burger Happy Meal", foodItems, HAPPY_MENU_DISCOUNT); return item(meal); > public Order build() < Order order = new Order(service, items, customerName); if (!validateOrder()) < System.out.println("Sorry! Order can't be placed without service type (Take Away/Eat Here) and any food item."); return null; >return order; > private boolean validateOrder() < return (service != null) && !items.isEmpty(); >>

Готово! Теперь пришло время написать Main для выполнения и тестирования результат:

package org.trishinfotech.builder; import org.trishinfotech.builder.beverages.BeverageSize; import org.trishinfotech.builder.beverages.cold.CocaCola; import org.trishinfotech.builder.beverages.cold.Pepsi; import org.trishinfotech.builder.beverages.hot.HotChocolate; import org.trishinfotech.builder.food.burger.ChickenBurger; import org.trishinfotech.builder.food.nuggets.CheeseNuggets; import org.trishinfotech.builder.food.nuggets.ChickenNuggets; import org.trishinfotech.builder.util.BillPrinter; public class Main < public static void main(String[] args) < OrderBuilder builder1 = new OrderBuilder(); // you can see the use of chained calls of setters here. No statement terminator // till we set all the values of the object Order meal1 = builder1.name("Brijesh").service(OrderService.TAKE_AWAY).item(new ChickenBurger()) .item(new Pepsi(BeverageSize.M)).vegNuggetsHappyMeal().build(); BillPrinter.printItemisedBill(meal1); OrderBuilder builder2 = new OrderBuilder(); Order meal2 = builder2.name("Micheal").service(OrderService.EAT_HERE).item(new ChickenNuggets()) .item(new CheeseNuggets()).item(new CocaCola(BeverageSize.L)).chickenBurgerHappyMeal() .item(new HotChocolate(BeverageSize.M)).vegBurgerHappyMeal().build(); BillPrinter.printItemisedBill(meal2); >>

А вот и результат работы программы:

 Food Court ================================================================================================================= Service: Take Away (2.00 Tax) Customer Name: Brijesh ----------------------------------------------------------------------------------------------------------------- Food Item | Calories | Packing | Price | Packing Price | Discount % | Total Price ----------------------------------------------------------------------------------------------------------------- Chicken Burger | 300 | Wrap | 4.50 | 0.40 | 0.00 | 4.90 Pepsi (M) | 220 | Bottle | 2.20 | 0.75 | 0.00 | 2.95 Veg Nuggets Happy Meal | 490 | Multi-Pack | 5.00 | 2.00 | 5.00 | 6.75 ================================================================================================================= Date: 2020/10/09 20:02:38 Total Bill (incl. taxes): 14.89 Enjoy your meal! Food Court ================================================================================================================= Service: Eat Here (5.50 Tax) Customer Name: Micheal ----------------------------------------------------------------------------------------------------------------- Food Item | Calories | Packing | Price | Packing Price | Discount % | Total Price ----------------------------------------------------------------------------------------------------------------- Chicken Nuggets | 450 | Container | 5.00 | 1.25 | 0.00 | 6.25 Cheese Nuggets | 330 | Container | 3.80 | 1.25 | 0.00 | 5.05 Coca-Cola (L) | 290 | Bottle | 2.00 | 0.75 | 0.00 | 2.75 Chicken Burger Happy Meal | 450 | Multi-Pack | 5.50 | 1.15 | 5.00 | 6.38 Hot Chocolate (M) | 450 | Sipper Mug | 2.30 | 1.60 | 0.00 | 3.90 Veg Burger Happy Meal | 340 | Multi-Pack | 3.90 | 1.15 | 5.00 | 4.86 ================================================================================================================= Date: 2020/10/09 20:02:38 Total Bill (incl. taxes): 30.78 Enjoy your meal!

Ну вот и все! Я надеюсь, что этот урок помог освоить паттерн Builder.

что такое builder ? (знаю что это связано с программированием)

WinLock Builder (англ. ) — строитель говна. ПО используемое тупой отмороженой на всю голову школотой с IQ ниже 35 для создания «»типа вирусов».

Остальные ответы

1 программный продукт, инструмент быстрой разработки
2 Программа блокирует систему и просит отправить смс с кодом на ваш номер, чтобы затем получить пароль разблокировки

Похожие вопросы
Ваш браузер устарел

Мы постоянно добавляем новый функционал в основной интерфейс проекта. К сожалению, старые браузеры не в состоянии качественно работать с современными программными продуктами. Для корректной работы используйте последние версии браузеров Chrome, Mozilla Firefox, Opera, Microsoft Edge или установите браузер Atom.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *