# Паттерн Строитель (Builder) в Rust: гибкое создание объектов

> Как применить паттерн проектирования Builder в языке Rust? Гайд по пошаговому конструированию сложных структур с валидацией полей и обработкой ошибок.

- **URL:** https://eagle2000.space/rust/rust-builder/
- **Date:** 2025-02-09

- **Tags:** rust, dev

---## Зачем нужны паттерны проектирования?
Паттерны проектирования — это проверенные временем решения типичных задач, которые возникают при разработке программного обеспечения. Они не являются строгими правилами, а скорее рекомендациями, которые помогают структурировать код, делая его более читаемым, поддерживаемым и расширяемым.

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

- Упростить поддержку кода: Чёткая структура делает код понятным для других разработчиков.
- Ускорить разработку: Не нужно изобретать велосипед — можно использовать готовые решения.
- Сделать код гибким: Паттерны позволяют легко вносить изменения, не ломая существующую логику.

Один из самых известных источников по паттернам — книга "Банды четырёх" ([Design Patterns: Elements of Reusable Object-Oriented Software](https://ru.wikipedia.org/wiki/Design_Patterns)). Однако когда я последний раз открывал её, примеры были приведены на C++ и Smalltalk. Согласитесь, что Smalltalk не самый актуальный язык на сегодня. Поэтому советую искать актуальные примеры на современных языках, таких как Rust, [Dart](https://github.com/scottt2/design-patterns-in-dart/blob/master/builder/builder.dart), [Python](https://github.com/faif/python-patterns/blob/master/patterns/creational/builder.py) или [Go](https://github.com/tmrts/go-patterns/blob/master/creational/builder.md), чтобы лучше понимать, как применять паттерны в реальных проектах.

## Паттерн "Строитель" в Rust
Паттерн "Строитель" (Builder) — это порождающий шаблон проектирования, который позволяет пошагово создавать сложные объекты. Он особенно полезен, когда объект имеет множество параметров, часть из которых может быть необязательной. В Rust, где строгая типизация и безопасность памяти являются ключевыми принципами, этот паттерн помогает писать чистый и поддерживаемый код.

## Пример из жизни: заказ бургера
Представьте, что вы заказываете бургер в маке (или как говорят в профессиональных кругах "макич"). Вы можете выбрать:

- Булочку (обязательно)
- Котлету (обязательно)
- Сыр (опционально)
- Салат (опционально)
- Соусы (опционально)

Паттерн "Строитель" идеально подходит для моделирования такого сценария.

-----

## Реализация на Rust

В Rust паттерн "Строитель" можно реализовать без создания отдельной структуры для строителя. Вместо этого мы добавляем методы конфигурации прямо к основной структуре.


### 💻 Паттерн Строитель в Rust


```rust
#[derive(Debug)]
struct Burger {
    bun: String,
    patty: String,
    cheese: Option<String>,
    salad: bool,
    sauce: Option<String>,
}

impl Burger {
    // Создаем новую реализацию с обязательными параметрами
    fn new(bun: String, patty: String) -> Self {
        Burger {
            bun,
            patty,
            cheese: None,
            salad: false,
            sauce: None,
        }
    }

    // Добавляем сыр
    fn add_cheese(mut self, cheese: String) -> Self {
        self.cheese = Some(cheese);
        self
    }

    // Добавляем салат
    fn add_salad(mut self) -> Self {
        self.salad = true;
        self
    }

    // Добавляем соус
    fn add_sauce(mut self, sauce: String) -> Self {
        self.sauce = Some(sauce);
        self
    }

    // Валидация и финализация объекта
    fn build(self) -> Result<Self, String> {
        if self.bun.is_empty() || self.patty.is_empty() {
            return Err("Булочка и котлета обязательны!".to_string());
        }
        Ok(self)
    }
}

fn main() {
    let burger = Burger::new("Булочка с кунжутом".to_string(), "Котлета из говядины".to_string())
        .add_cheese("Чеддер".to_string())
        .add_salad()
        .add_sauce("Барбекю".to_string())
        .build();

    match burger {
        Ok(burger) => println!("Ваш бургер: {:?}", burger),
        Err(e) => println!("Ошибка: {}", e),
    }
}
```

**Реализация паттерна "Строитель" (Builder)**
- Конструируем сложный объект `Burger` пошагово с помощью цепочки вызовов.
- Метод `build()` выполняет валидацию и возвращает `Result`.


### Как это работает?
1. Методы конфигурации:
   Каждый метод (add_cheese, add_salad, add_sauce) возвращает self, что позволяет использовать цепочку вызовов.
2. Валидация в build:
   Метод build проверяет, что обязательные поля (bun и patty) заполнены. Если всё в порядке, он возвращает Ok(Self), иначе — ошибку.
3. Иммутабельность:
   Каждый метод создает новый экземпляр структуры с обновленными значениями, что соответствует философии Rust.

## Преимущества паттерна "Строитель"
1. Гибкость:
   Вы можете добавлять только те параметры, которые нужны, оставляя остальные по умолчанию.
2. Валидация:
   Метод build гарантирует, что объект будет создан только с корректными данными.
3. Читаемость:
   Цепочка вызовов методов делает код интуитивно понятным.
4. Подход похож на лего:
   Мы конструируем объект из кубиков.

---

## Когда стоит использовать?
* Когда объект имеет много параметров, часть из которых необязательна.
* Когда важно обеспечить валидацию данных перед созданием объекта.
* Когда вы хотите сделать код гибким и легко расширяемым.

---

## Заключение
Паттерн "Строитель" (Builder) — это ваш помощник в проектировании объектов. Конечно это не волшебная таблетка, это всего лишь ваш инструмент. В Rust его можно реализовать как с использованием отдельной структуры, так и без неё. Выбор зависит от сложности задачи и ваших предпочтений.

Паттерны проектирования, такие как "Строитель" (Builder), помогают писать код, который легче поддерживать и расширять. И хотя можно обойтись и без них, их использование делает разработку более предсказуемой и менее стрессовой. Попробуйте этот паттерн в своих проектах, чтобы убедиться в его эффективности! 🚀
