This commit is contained in:
parent
ce9f2d25e7
commit
46e423c2db
318
dev/architecture/Builder Pattern.md
Normal file
318
dev/architecture/Builder Pattern.md
Normal file
@ -0,0 +1,318 @@
|
||||
---
|
||||
aliases:
|
||||
tags:
|
||||
- maturity/🌱
|
||||
date: 2024-10-04
|
||||
zero-link:
|
||||
parents:
|
||||
linked:
|
||||
---
|
||||
Builder Pattern — это [[порождающий паттерн проектирования]], который используется для пошагового создания сложных объектов. Этот паттерн особенно полезен, когда объект может иметь множество конфигураций или параметров, которые делают его создание через конструкторы неудобным или даже невозможным.
|
||||
|
||||
Основные концепции
|
||||
- **Разделение построения и представления**. Билдер позволяет отделить логику создания объекта от его конечной структуры. Это делает код более чистым и поддерживаемым.
|
||||
- **Пошаговая сборка**. Позволяет добавлять параметры или части объекта последовательно, при этом сам процесс создания может контролироваться и меняться независимо от основной логики объекта.
|
||||
- **Иммутабельность**. Паттерн билдер часто применяется для создания неизменяемых объектов. После сборки объекта его нельзя изменить, что улучшает предсказуемость и безопасность.
|
||||
|
||||
## Пример применения
|
||||
Рассмотрим создание объекта `Car`, у которого много настроек, таких как тип двигателя, количество дверей, цвет и т.д.
|
||||
|
||||
Без использования паттерна «Билдер» мы можем столкнуться с такой проблемой: необходимо создавать различные конструкторы, что ухудшает читаемость и поддержку кода. Паттерн «Билдер» помогает избежать этого, при этом используя [[Fluent API|fluent API]] стиль — подход, при котором методы возвращают сам объект билдера, позволяя вызывать их цепочкой. Это делает код более выразительным и легким для чтения.
|
||||
|
||||
```java
|
||||
public class Car {
|
||||
private String engine;
|
||||
private int doors;
|
||||
private String color;
|
||||
|
||||
private Car(CarBuilder builder) {
|
||||
this.engine = builder.engine;
|
||||
this.doors = builder.doors;
|
||||
this.color = builder.color;
|
||||
}
|
||||
|
||||
public static class CarBuilder {
|
||||
private String engine;
|
||||
private int doors;
|
||||
private String color;
|
||||
|
||||
public CarBuilder setEngine(String engine) {
|
||||
this.engine = engine;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CarBuilder setDoors(int doors) {
|
||||
this.doors = doors;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CarBuilder setColor(String color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Car build() {
|
||||
return new Car(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Использование паттерна:
|
||||
```java
|
||||
Car car = new Car.CarBuilder()
|
||||
.setEngine("V8")
|
||||
.setDoors(4)
|
||||
.setColor("Red")
|
||||
.build();
|
||||
|
||||
System.out.println(car);
|
||||
```
|
||||
|
||||
**Преимущества**
|
||||
1. **Чистый код**. Конфигурация объектов становится ясной и понятной, даже если у объекта множество параметров.
|
||||
2. **Гибкость в создании объектов**. Можно не указывать все параметры сразу, а добавлять их по мере необходимости, что делает процесс сборки более гибким.
|
||||
3. **Поддержка иммутабельности**. Объекты могут быть неизменяемыми после создания, так как параметры устанавливаются только в процессе сборки.
|
||||
4. **Минимизация перегрузок конструкторов**. Это позволяет избежать множества конструкторов для различных комбинаций параметров.
|
||||
|
||||
**Недостатки**
|
||||
- **Усложнение кода**. Добавление класса-билдера может увеличить объем кода, особенно если объект не настолько сложен, чтобы оправдать использование паттерна.
|
||||
- **Многословность**. Если объект требует только нескольких параметров, то использование билдера может казаться излишним и создавать ненужную многословность.
|
||||
|
||||
**Когда применять?**
|
||||
- Когда объект имеет множество конфигураций и параметров.
|
||||
- Когда нужен гибкий процесс создания объектов с возможностью пошагового добавления параметров.
|
||||
- Когда объект должен быть неизменяемым, и после сборки его состояние не должно меняться.
|
||||
|
||||
## Продвинутый билдер
|
||||
### Обязательные поля
|
||||
Одной из распространенных проблем является ==отсутствие явного указания на обязательные поля== при использовании билдера. Если объект имеет поля, которые обязательны для заполнения (например, идентификатор или имя), но их установка не контролируется билдером, это может привести к созданию некорректных или невалидных объектов.
|
||||
|
||||
**Решение**:
|
||||
- Обязательные поля можно передавать через конструктор билдера, чтобы их указание было обязательным. Остальные параметры можно указывать через цепочку методов.
|
||||
|
||||
Пример:
|
||||
```java
|
||||
public class Car {
|
||||
private final String engine; // Обязательное поле
|
||||
private final String model; // Обязательное поле
|
||||
private int doors; // Необязательное поле
|
||||
private String color; // Необязательное поле
|
||||
|
||||
// Приватный конструктор для сборки объекта через билдер
|
||||
private Car(CarBuilder builder) {
|
||||
this.engine = builder.engine;
|
||||
this.model = builder.model;
|
||||
this.doors = builder.doors;
|
||||
this.color = builder.color;
|
||||
}
|
||||
|
||||
// Статический метод для создания билдера с обязательными полями
|
||||
public static CarBuilder builder(String engine, String model) {
|
||||
return new CarBuilder(engine, model);
|
||||
}
|
||||
|
||||
public static class CarBuilder {
|
||||
private final String engine; // Обязательное поле
|
||||
private final String model; // Обязательное поле
|
||||
private int doors = 4; // Значение по умолчанию
|
||||
private String color = "Black"; // Значение по умолчанию
|
||||
|
||||
// Конструктор билдера с обязательными полями
|
||||
public CarBuilder(String engine, String model) {
|
||||
if (engine == null || engine.isEmpty()) {
|
||||
throw new IllegalArgumentException("Engine is required");
|
||||
}
|
||||
if (model == null || model.isEmpty()) {
|
||||
throw new IllegalArgumentException("Model is required");
|
||||
}
|
||||
this.engine = engine;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
// Методы для установки необязательных полей
|
||||
public CarBuilder setDoors(int doors) {
|
||||
this.doors = doors;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CarBuilder setColor(String color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Метод для сборки объекта Car
|
||||
public Car build() {
|
||||
return new Car(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Теперь обязательные поля передаются при создании билдера:
|
||||
```java
|
||||
Car car = Car.builder("V8", "Sedan") // Передача обязательных полей через статический метод
|
||||
.setDoors(2) // Опциональные поля
|
||||
.setColor("Red")
|
||||
.build();
|
||||
```
|
||||
### Валидация создания объекта
|
||||
Ещё одна частая проблема заключается в том, что во время процесса построения ==не проверяются ограничения на совместимость полей.== Например, не всегда проверяется корректность значений или логика взаимодействия между параметрами, что может привести к созданию некорректного объекта.
|
||||
|
||||
**Решение**:
|
||||
Добавляйте в билдер логику валидации и проверки состояния перед созданием объекта. Это позволит убедиться, что все параметры совместимы и объект корректен.
|
||||
|
||||
Пример:
|
||||
```java
|
||||
public Car build() {
|
||||
if (doors < 2 || doors > 6) {
|
||||
throw new IllegalArgumentException("Invalid number of doors");
|
||||
}
|
||||
return new Car(this);
|
||||
}
|
||||
```
|
||||
### Многократный вызов методов
|
||||
Когда методы билдера вызываются несколько раз, каждый вызов может перезаписывать значение параметра, что может остаться незамеченным или вызвать непредсказуемое поведение.
|
||||
|
||||
Предположим, что у нас есть билдер для создания объекта `Car`, и метод для установки количества дверей (`setDoors`) был вызван дважды:
|
||||
```java
|
||||
Car car = new Car.CarBuilder("V8")
|
||||
.setDoors(4)
|
||||
.setDoors(2) // Этот вызов перезапишет предыдущее значение
|
||||
.setColor("Red")
|
||||
.build();
|
||||
```
|
||||
|
||||
В результате объект car будет создан с двумя дверями, хотя программист мог ожидать, что будет 4 двери (из-за первого вызова). Такая ситуация особенно распространена, когда объект конфигурируется динамически, или когда несколько разработчиков работают с билдером, не зная всех деталей.
|
||||
#### Возможные решения проблемы
|
||||
**Введение проверок на повторный вызов.** Чтобы избежать многократных вызовов одного и того же метода, можно добавить логику проверки, которая будет отслеживать, был ли метод уже вызван ранее. Если метод вызывается повторно, можно выбросить исключение или проигнорировать повторный вызов.
|
||||
|
||||
```java
|
||||
public static class CarBuilder {
|
||||
private String engine;
|
||||
private int doors;
|
||||
private String color;
|
||||
private boolean doorsSet = false; // Флаг, указывающий на то, что метод setDoors уже был вызван
|
||||
|
||||
public CarBuilder(String engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
public CarBuilder setDoors(int doors) {
|
||||
if (doorsSet) {
|
||||
throw new IllegalStateException("Doors can only be set once");
|
||||
}
|
||||
this.doors = doors;
|
||||
doorsSet = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CarBuilder setColor(String color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Car build() {
|
||||
return new Car(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Логирование перезаписи параметров.** Если необходимо разрешить многократные вызовы методов, но при этом важно отслеживать перезапись параметров, можно добавлять логирование, чтобы было видно, когда параметр перезаписывается новым значением.
|
||||
|
||||
```java
|
||||
public CarBuilder setDoors(int doors) {
|
||||
if (this.doors != 0) {
|
||||
System.out.println("Warning: Doors value is being overwritten from " + this.doors + " to " + doors);
|
||||
}
|
||||
this.doors = doors;
|
||||
return this;
|
||||
}
|
||||
```
|
||||
|
||||
**Использование** [[Fluent API#Step building|Fluent API Step building]]. Позволит конфигурировать объект в определенной последовательности.
|
||||
|
||||
```java
|
||||
public class Car {
|
||||
private final String engine; // Обязательное поле
|
||||
private final int doors; // Обязательное поле
|
||||
private final String color; // Обязательное поле
|
||||
|
||||
// Приватный конструктор для сборки через пошаговую сборку
|
||||
private Car(String engine, int doors, String color) {
|
||||
this.engine = engine;
|
||||
this.doors = doors;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
// Интерфейс для первого шага: выбор двигателя
|
||||
public interface EngineStep {
|
||||
DoorsStep setEngine(String engine);
|
||||
}
|
||||
|
||||
// Интерфейс для второго шага: выбор дверей
|
||||
public interface DoorsStep {
|
||||
ColorStep setDoors(int doors);
|
||||
}
|
||||
|
||||
// Интерфейс для третьего шага: выбор цвета
|
||||
public interface ColorStep {
|
||||
BuildStep setColor(String color);
|
||||
}
|
||||
|
||||
// Интерфейс для финального шага: завершение сборки
|
||||
public interface BuildStep {
|
||||
Car build();
|
||||
}
|
||||
|
||||
// Класс, который реализует пошаговую сборку
|
||||
public static class Builder implements EngineStep, DoorsStep, ColorStep, BuildStep {
|
||||
private String engine;
|
||||
private int doors;
|
||||
private String color;
|
||||
|
||||
@Override
|
||||
public DoorsStep setEngine(String engine) {
|
||||
this.engine = engine;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStep setDoors(int doors) {
|
||||
this.doors = doors;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildStep setColor(String color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Car build() {
|
||||
return new Car(engine, doors, color);
|
||||
}
|
||||
}
|
||||
|
||||
// Метод для запуска пошаговой сборки
|
||||
public static EngineStep builder() {
|
||||
return new Builder();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
## Мета информация
|
||||
**Область**:: [[../../meta/zero/00 Разработка|00 Разработка]]
|
||||
**Родитель**:: [[Порождающий паттерн проектирования]]
|
||||
**Источник**::
|
||||
**Создана**:: [[2024-10-04]]
|
||||
**Автор**::
|
||||
### Дополнительные материалы
|
||||
-
|
||||
|
||||
### Дочерние заметки
|
||||
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
|
@ -9,7 +9,7 @@ zero-link:
|
||||
- "[[../../meta/zero/00 Архитектура ПО|00 Архитектура ПО]]"
|
||||
parents:
|
||||
- "[[Inversion of Control]]"
|
||||
- "[[Паттерн программирования]]"
|
||||
- "[[Паттерн проектирования]]"
|
||||
linked:
|
||||
---
|
||||
**Dependency Injection (DI)** — это паттерн проектирования, который используется для реализации принципа [[Inversion of Control]] (IoC). DI позволяет передавать зависимости объектам извне, вместо того чтобы объекты сами создавали их. Это ослабляет связь между компонентами системы, что делает код более гибким и удобным для поддержки.
|
||||
@ -60,7 +60,7 @@ class Car {
|
||||
***
|
||||
## Мета информация
|
||||
**Область**:: [[../../meta/zero/00 Архитектура ПО|00 Архитектура ПО]]
|
||||
**Родитель**:: [[Inversion of Control]], [[Паттерн программирования]]
|
||||
**Родитель**:: [[Inversion of Control]], [[Паттерн проектирования]]
|
||||
**Источник**::
|
||||
**Автор**::
|
||||
**Создана**:: [[2023-10-06]]
|
||||
|
356
dev/architecture/Fluent API.md
Normal file
356
dev/architecture/Fluent API.md
Normal file
@ -0,0 +1,356 @@
|
||||
---
|
||||
aliases:
|
||||
tags:
|
||||
- maturity/🌱
|
||||
date: 2024-10-04
|
||||
zero-link:
|
||||
parents:
|
||||
linked:
|
||||
---
|
||||
**Fluent API** — это стиль проектирования API, в котором ==методы возвращают объект, к которому они принадлежат==, позволяя вызывать методы цепочкой (chaining).
|
||||
|
||||
**Основные концепции**
|
||||
- **Method Chaining**. Fluent API позволяет вызывать методы один за другим, что уменьшает количество промежуточных переменных и улучшает читаемость.
|
||||
- **Самоописывающийся код**. Использование цепочки методов делает код более понятным и логичным, приближая его к естественному языку.
|
||||
|
||||
**Где встречается?**
|
||||
- Фреймворки с реактивным подходом.
|
||||
- Java Stream
|
||||
- **Библиотеки для работы с базами данных**. Такие фреймворки, как JPA или Hibernate, используют Fluent API для создания запросов. Например, запросы могут выглядеть так
|
||||
|
||||
```java
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Car> query = builder.createQuery(Car.class);
|
||||
query.select(query.from(Car.class))
|
||||
.where(builder.equal(root.get("color"), "Red"));
|
||||
```
|
||||
|
||||
- **Настройка объектов**. Fluent API часто используется в [[../../../../garden/ru/dev/architecture/Builder Pattern|Builder Pattern]], где объект строится поэтапно через цепочку методов.
|
||||
- Конфигурация. Например Spring Security, Kafka Streams
|
||||
- **Фреймворки для тестирования**. Например, в JUnit или AssertJ можно строить цепочки утверждений:
|
||||
|
||||
Fluent API часто используется для построения специфических языков (DSL), которые имитируют человеческий язык и делают код максимально самоописательным.
|
||||
|
||||
Пример императивного кода
|
||||
```java
|
||||
Instant start = Instant.now();
|
||||
Duration timeout = Duration.ofSeconds(10);
|
||||
do {
|
||||
Thread.sleep(200);
|
||||
var entity = repo.get("id");
|
||||
if ("EXPECTED".equals(entity.status)) {
|
||||
return;
|
||||
}
|
||||
} while (Instant.now().isBefore(start.plus(timeout)));
|
||||
throw new AssertionError("Status was not updated to EXPECTED");
|
||||
```
|
||||
|
||||
И аналогичный в стиле Fluent API
|
||||
```java
|
||||
Awaitility.await("Entity status should be updated to EXPECTED")
|
||||
.atMost(Duration.ofSeconds(10))
|
||||
.pollDelay(Duration.ofMillis(200))
|
||||
.until(() -> "EXPECTED".equals(repo.get("id").status));
|
||||
```
|
||||
## Приемы и подходы
|
||||
### Method chaining
|
||||
**Method chaining** — это техника, при которой методы возвращают текущий объект (обычно через `this`), позволяя вызывать несколько методов последовательно в одной строке.
|
||||
|
||||
```java {7, 12}
|
||||
public class Car {
|
||||
private String engine;
|
||||
private int doors;
|
||||
|
||||
public Car setEngine(String engine) {
|
||||
this.engine = engine;
|
||||
return this; // Возвращаем текущий объект
|
||||
}
|
||||
|
||||
public Car setDoors(int doors) {
|
||||
this.doors = doors;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
```
|
||||
### Смена контекста
|
||||
#### С помощью method chaining
|
||||
Представим, что мы настраиваем серверное приложение с несколькими аспектами: базовая настройка, настройка безопасности, логирования и т.д. Здесь каждый вызов метода переключает нас на новый “контекст”, где мы продолжаем настраивать приложение, но в рамках другой области (например, с безопасности переключаемся на логирование).
|
||||
|
||||
```java
|
||||
public class ServerConfig {
|
||||
|
||||
public ServerConfig http() {
|
||||
System.out.println("HTTP basic configuration");
|
||||
return this; // Возвращаем тот же объект для продолжения цепочки
|
||||
}
|
||||
|
||||
public ServerConfig security() {
|
||||
System.out.println("Security configuration");
|
||||
return this; // Переключение на контекст безопасности
|
||||
}
|
||||
|
||||
public ServerConfig authorizeRequests() {
|
||||
System.out.println("Authorization configuration");
|
||||
return this; // Переключение на настройку авторизации запросов
|
||||
}
|
||||
|
||||
public ServerConfig requestMatchers(String pattern) {
|
||||
System.out.println("Configuring request matchers for: " + pattern);
|
||||
return this; // Продолжение работы в контексте авторизации
|
||||
}
|
||||
|
||||
public ServerConfig csrf() {
|
||||
System.out.println("CSRF protection disabled");
|
||||
return this; // Переключение на настройку защиты CSRF
|
||||
}
|
||||
|
||||
public ServerConfig exceptionHandling() {
|
||||
System.out.println("Exception handling configuration");
|
||||
return this; // Переключение на обработку исключений
|
||||
}
|
||||
|
||||
public Server build() {
|
||||
System.out.println("Server is configured and built");
|
||||
return new Server();
|
||||
}
|
||||
}
|
||||
|
||||
class Server {
|
||||
// Имитация запущенного сервера
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
Server server = new ServerConfig()
|
||||
.http() // Контекст базовой настройки HTTP
|
||||
.security() // Переключение на контекст безопасности
|
||||
.authorizeRequests() // Настройка авторизации запросов
|
||||
.requestMatchers("/") // Настройка доступа для главной страницы
|
||||
.requestMatchers("/api") // Настройка доступа к API
|
||||
.csrf() // Отключение CSRF
|
||||
.exceptionHandling() // Настройка обработки исключений
|
||||
.build(); // Завершаем конфигурацию и запускаем сервер
|
||||
```
|
||||
#### С помощью лямбда-выражений
|
||||
```java
|
||||
public class ServerConfig {
|
||||
|
||||
public ServerConfig http(Consumer<HttpConfig> httpConfig) {
|
||||
System.out.println("Entering HTTP configuration context");
|
||||
httpConfig.accept(new HttpConfig());
|
||||
return this; // Возвращаем тот же объект для дальнейшей конфигурации
|
||||
}
|
||||
|
||||
public ServerConfig security(Consumer<SecurityConfig> securityConfig) {
|
||||
System.out.println("Entering Security configuration context");
|
||||
securityConfig.accept(new SecurityConfig());
|
||||
return this; // Переключение на контекст безопасности
|
||||
}
|
||||
|
||||
public ServerConfig logging(Consumer<LoggingConfig> loggingConfig) {
|
||||
System.out.println("Entering Logging configuration context");
|
||||
loggingConfig.accept(new LoggingConfig());
|
||||
return this; // Переключение на контекст логирования
|
||||
}
|
||||
|
||||
public Server build() {
|
||||
System.out.println("Server is configured and built");
|
||||
return new Server(); // Финальный этап — запуск сервера
|
||||
}
|
||||
|
||||
// Вложенные классы конфигураций для разных контекстов
|
||||
public static class HttpConfig {
|
||||
public HttpConfig enableHttp2() {
|
||||
System.out.println("HTTP/2 enabled");
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpConfig port(int port) {
|
||||
System.out.println("Server will listen on port: " + port);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SecurityConfig {
|
||||
public SecurityConfig enableTLS() {
|
||||
System.out.println("TLS enabled");
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityConfig authorizeRequests(Consumer<RequestAuthorization> authorizationConfig) {
|
||||
System.out.println("Authorizing requests...");
|
||||
authorizationConfig.accept(new RequestAuthorization());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoggingConfig {
|
||||
public LoggingConfig level(String level) {
|
||||
System.out.println("Logging level set to: " + level);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequestAuthorization {
|
||||
public RequestAuthorization permitAll() {
|
||||
System.out.println("All requests are permitted");
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestAuthorization authenticated() {
|
||||
System.out.println("Authenticated requests only");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
Server server = new ServerConfig()
|
||||
.http(http -> http.enableHttp2().port(8080)) // Настройка HTTP с использованием лямбда-выражения
|
||||
.security(security -> security
|
||||
.enableTLS() // Настройка безопасности
|
||||
.authorizeRequests(auth -> auth.authenticated())) // Смена контекста внутри лямбды
|
||||
.logging(log -> log.level("INFO")) // Настройка логирования с помощью лямбда
|
||||
.build(); // Финальная сборка сервера
|
||||
```
|
||||
|
||||
### Step building
|
||||
Позволяет организовать процесс создания объектов или выполнения операций через строго упорядоченные шаги. Хотя этот подход часто используется в [[../../../../garden/ru/dev/architecture/Builder Pattern|Builder Pattern]], он применим и в других контекстах, например, при вызове API, конфигурации сложных процессов, построении запросов и даже в рабочих процессах (workflow).
|
||||
|
||||
**Основные концепции**
|
||||
- **Упорядоченные шаги**. Процесс выполнения операции или создания объекта разделен на несколько этапов (шагов), которые должны выполняться в определённой последовательности. Каждый шаг может представлять собой настройку, изменение состояния или выполнение отдельной операции.
|
||||
- **Контроль обязательных шагов**. Пошаговая сборка гарантирует, что определенные важные шаги не будут пропущены. Это особенно полезно для процессов, где важно соблюдение последовательности действий или конфигурации обязательных параметров.
|
||||
|
||||
**Примеры применения пошаговой сборки**
|
||||
|
||||
**Построение SQL-запросов**
|
||||
|
||||
```java
|
||||
public interface SelectStep {
|
||||
FromStep select(String... columns);
|
||||
}
|
||||
|
||||
public interface FromStep {
|
||||
WhereStep from(String table);
|
||||
}
|
||||
|
||||
public interface WhereStep {
|
||||
OrderByStep where(String condition);
|
||||
}
|
||||
|
||||
public interface OrderByStep {
|
||||
BuildStep orderBy(String column);
|
||||
}
|
||||
|
||||
public interface BuildStep {
|
||||
String build();
|
||||
}
|
||||
|
||||
public class SqlQueryBuilder implements SelectStep, FromStep, WhereStep, OrderByStep, BuildStep {
|
||||
private String query;
|
||||
|
||||
@Override
|
||||
public FromStep select(String... columns) {
|
||||
query = "SELECT " + String.join(", ", columns);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WhereStep from(String table) {
|
||||
query += " FROM " + table;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderByStep where(String condition) {
|
||||
query += " WHERE " + condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildStep orderBy(String column) {
|
||||
query += " ORDER BY " + column;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String build() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public static SelectStep start() {
|
||||
return new SqlQueryBuilder();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Самообобщение
|
||||
Когда мы используем наследование для создания подклассов, возникает проблема, что методы Fluent API могут возвращать не подкласс, а базовый класс, разрывая цепочку вызовов. **Самообобщение** решает эту проблему, позволяя методам возвращать правильный тип подкласса.
|
||||
|
||||
**Пример проблемы без самообобщения**. Допустим, у нас есть базовый класс с цепочкой методов, и мы хотим унаследовать этот класс.
|
||||
|
||||
```java
|
||||
class BaseBuilder {
|
||||
public BaseBuilder setName(String name) {
|
||||
System.out.println("Name set to: " + name);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class AdvancedBuilder extends BaseBuilder {
|
||||
public AdvancedBuilder setFeature(String feature) {
|
||||
System.out.println("Feature set to: " + feature);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
AdvancedBuilder builder = new AdvancedBuilder();
|
||||
builder.setName("MyObject")
|
||||
.setFeature("AdvancedFeature"); // Ошибка: возвращается BaseBuilder
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
В этом примере `setName()` возвращает тип `BaseBuilder`, поэтому попытка вызвать `setFeature()` на результат этого вызова приведет к ошибке. Метод `setFeature()` будет недоступен.
|
||||
|
||||
**Решение с использованием самообобщения (Self-type Generics)**. Мы можем решить эту проблему, используя самообобщение с помощью обобщений (generics). Это позволит методам возвращать **самый специфичный тип**.
|
||||
|
||||
```java
|
||||
class BaseBuilder<T extends BaseBuilder<T>> {
|
||||
public T setName(String name) {
|
||||
System.out.println("Name set to: " + name);
|
||||
return (T) this; // Возвращаем текущий объект с типом T
|
||||
}
|
||||
}
|
||||
|
||||
class AdvancedBuilder extends BaseBuilder<AdvancedBuilder> {
|
||||
public AdvancedBuilder setFeature(String feature) {
|
||||
System.out.println("Feature set to: " + feature);
|
||||
return this; // Возвращаем текущий объект с типом AdvancedBuilder
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
AdvancedBuilder builder = new AdvancedBuilder();
|
||||
builder.setName("MyObject")
|
||||
.setFeature("AdvancedFeature"); // Теперь работает правильно
|
||||
}
|
||||
}
|
||||
```
|
||||
***
|
||||
## Мета информация
|
||||
**Область**:: [[../../../../garden/ru/meta/zero/00 Java разработка|00 Java разработка]]
|
||||
**Родитель**::
|
||||
**Источник**::
|
||||
**Создана**:: [[2024-10-04]]
|
||||
**Автор**::
|
||||
### Дополнительные материалы
|
||||
-
|
||||
|
||||
### Дочерние заметки
|
||||
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
|
@ -25,7 +25,7 @@ linked:
|
||||
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
<!-- SerializedQuery: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
- [[Inversion of Control]]
|
||||
- [[Паттерн программирования]]
|
||||
- [[Один клиент — один поток]]
|
||||
- [[Много клиентов — один поток]]
|
||||
- [[Один клиент — один поток]]
|
||||
- [[Паттерн проектирования]]
|
||||
<!-- SerializedQuery END -->
|
||||
|
@ -11,6 +11,8 @@ parents:
|
||||
linked:
|
||||
---
|
||||
- [[Dependency Injection]]
|
||||
- [[Порождающий паттерн проектирования]]
|
||||
- [[Builder Pattern|Builder Pattern]]
|
||||
|
||||
***
|
||||
## Мета информация
|
||||
@ -25,4 +27,5 @@ linked:
|
||||
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
<!-- SerializedQuery: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
- [[Dependency Injection]]
|
||||
- [[Порождающий паттерн проектирования]]
|
||||
<!-- SerializedQuery END -->
|
26
dev/architecture/Порождающий паттерн проектирования.md
Normal file
26
dev/architecture/Порождающий паттерн проектирования.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
aliases:
|
||||
tags:
|
||||
- maturity/🌱
|
||||
date: 2024-10-04
|
||||
zero-link:
|
||||
parents:
|
||||
linked:
|
||||
---
|
||||
- [[Builder Pattern]]
|
||||
***
|
||||
## Мета информация
|
||||
**Область**:: [[../../meta/zero/00 Разработка|00 Разработка]]
|
||||
**Родитель**:: [[Паттерн проектирования]]
|
||||
**Источник**::
|
||||
**Создана**:: [[2024-10-04]]
|
||||
**Автор**::
|
||||
### Дополнительные материалы
|
||||
-
|
||||
|
||||
### Дочерние заметки
|
||||
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
<!-- SerializedQuery: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|
||||
- [[Builder Pattern]]
|
||||
<!-- SerializedQuery END -->
|
||||
|
@ -6,7 +6,7 @@ zero-link:
|
||||
---
|
||||
- [[../../dev/architecture/Архитектурный слой|Архитектурный слой]]
|
||||
- [[../../dev/architecture/Архитектурная концепция|Архитектурная концепция]]
|
||||
- [[../../dev/architecture/Паттерн программирования|Паттерн программирования]]
|
||||
- [[../../dev/architecture/Паттерн проектирования|Паттерн проектирования]]
|
||||
|
||||
Архитектурные ошибки и проблемы:
|
||||
- [[../../dev/architecture/Протекание абстракций|Протекание абстракций]]
|
||||
|
@ -94,7 +94,7 @@ NIO посылает события Netty их преобразовывает в
|
||||
-
|
||||
***
|
||||
## Мета информация
|
||||
**Область**::
|
||||
**Область**:: [[../00 Источники|00 Источники]]
|
||||
**Родитель**::
|
||||
**Источник**::
|
||||
**Создана**:: [[2024-10-02]]
|
||||
|
Loading…
Reference in New Issue
Block a user