← Назад к вопросам

Что такое паттерн Строитель?

2.0 Middle🔥 132 комментариев
#ООП и паттерны

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн Строитель (Builder)

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

Зачем нужен Строитель

Об объекты с множеством параметров сложны в использовании:

// СЛОЖНО — много параметров, легко ошибиться
final user = User(
  'John',
  'john@example.com',
  '+1234567890',
  'New York',
  30,
  true,
  false,
  null,
  null,
  true,
);

Строитель решает эту проблему:

// ПРОСТО и ПОНЯТНО
final user = UserBuilder()
  .setName('John')
  .setEmail('john@example.com')
  .setPhone('+1234567890')
  .setAge(30)
  .setEmailVerified(true)
  .build();

Структура паттерна

// 1. Класс, который нужно создать (объект)
class Pizza {
  final String size;
  final String crust;
  final List<String> toppings;
  final bool extraCheese;
  final bool spicy;
  
  Pizza({
    required this.size,
    required this.crust,
    required this.toppings,
    required this.extraCheese,
    required this.spicy,
  });
  
  @override
  String toString() {
    return 'Pizza(size: $size, crust: $crust, toppings: $toppings, extraCheese: $extraCheese, spicy: $spicy)';
  }
}

// 2. Строитель
class PizzaBuilder {
  late String _size;
  late String _crust;
  List<String> _toppings = [];
  bool _extraCheese = false;
  bool _spicy = false;
  
  PizzaBuilder size(String size) {
    if (size != 'S' && size != 'M' && size != 'L') {
      throw ArgumentError('Invalid size');
    }
    _size = size;
    return this;
  }
  
  PizzaBuilder crust(String crust) {
    if (crust != 'thin' && crust != 'thick' && crust != 'stuffed') {
      throw ArgumentError('Invalid crust');
    }
    _crust = crust;
    return this;
  }
  
  PizzaBuilder addTopping(String topping) {
    _toppings.add(topping);
    return this;
  }
  
  PizzaBuilder addToppings(List<String> toppings) {
    _toppings.addAll(toppings);
    return this;
  }
  
  PizzaBuilder extraCheese(bool value) {
    _extraCheese = value;
    return this;
  }
  
  PizzaBuilder spicy(bool value) {
    _spicy = value;
    return this;
  }
  
  Pizza build() {
    if (_size == null) throw StateError('Size is required');
    if (_crust == null) throw StateError('Crust is required');
    
    return Pizza(
      size: _size,
      crust: _crust,
      toppings: _toppings,
      extraCheese: _extraCheese,
      spicy: _spicy,
    );
  }
}

// Использование
final pizza = PizzaBuilder()
  .size('L')
  .crust('thick')
  .addTopping('pepperoni')
  .addTopping('mushrooms')
  .extraCheese(true)
  .spicy(true)
  .build();

print(pizza);

Пример: сложный SQL запрос

class QueryBuilder {
  late String _table;
  List<String> _select = ['*'];
  List<String> _where = [];
  List<String> _orderBy = [];
  int? _limit;
  int? _offset;
  
  QueryBuilder from(String table) {
    _table = table;
    return this;
  }
  
  QueryBuilder select(List<String> columns) {
    _select = columns;
    return this;
  }
  
  QueryBuilder where(String condition) {
    _where.add(condition);
    return this;
  }
  
  QueryBuilder orderBy(String column, {bool ascending = true}) {
    final direction = ascending ? 'ASC' : 'DESC';
    _orderBy.add('$column $direction');
    return this;
  }
  
  QueryBuilder limit(int count) {
    _limit = count;
    return this;
  }
  
  QueryBuilder offset(int count) {
    _offset = count;
    return this;
  }
  
  String build() {
    final query = StringBuffer('SELECT ');
    query.write(_select.join(', '));
    query.write(' FROM $_table');
    
    if (_where.isNotEmpty) {
      query.write(' WHERE ');
      query.write(_where.join(' AND '));
    }
    
    if (_orderBy.isNotEmpty) {
      query.write(' ORDER BY ');
      query.write(_orderBy.join(', '));
    }
    
    if (_limit != null) {
      query.write(' LIMIT $_limit');
    }
    
    if (_offset != null) {
      query.write(' OFFSET $_offset');
    }
    
    return query.toString();
  }
}

// Использование
final query = QueryBuilder()
  .from('users')
  .select(['id', 'name', 'email'])
  .where('age >= 18')
  .where('country = "USA"')
  .orderBy('name')
  .limit(10)
  .offset(5)
  .build();

print(query);
// SELECT id, name, email FROM users WHERE age >= 18 AND country = "USA" ORDER BY name ASC LIMIT 10 OFFSET 5

Builder в контексте Flutter

class DialogBuilder {
  late String _title;
  late String _message;
  final List<DialogButton> _buttons = [];
  String? _iconPath;
  bool _dismissible = true;
  
  DialogBuilder title(String title) {
    _title = title;
    return this;
  }
  
  DialogBuilder message(String message) {
    _message = message;
    return this;
  }
  
  DialogBuilder addButton(String label, VoidCallback onPressed) {
    _buttons.add(DialogButton(label: label, onPressed: onPressed));
    return this;
  }
  
  DialogBuilder icon(String path) {
    _iconPath = path;
    return this;
  }
  
  DialogBuilder dismissible(bool value) {
    _dismissible = value;
    return this;
  }
  
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(_title),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          if (_iconPath != null) Image.asset(_iconPath!, height: 64),
          Text(_message),
        ],
      ),
      actions: _buttons.map((btn) {
        return TextButton(
          onPressed: btn.onPressed,
          child: Text(btn.label),
        );
      }).toList(),
    );
  }
}

class DialogButton {
  final String label;
  final VoidCallback onPressed;
  
  DialogButton({required this.label, required this.onPressed});
}

// Использование в Flutter
showDialog(
  context: context,
  builder: (context) {
    return DialogBuilder()
      .title('Подтверждение')
      .message('Вы уверены?')
      .icon('assets/confirm.png')
      .addButton('Отмена', () => Navigator.pop(context))
      .addButton('Согласен', () {
        // обработка
        Navigator.pop(context);
      })
      .build(context);
  },
);

Преимущества Builder паттерна

  • Читаемость — код очень понятный и самодокументирующийся
  • Гибкость — можно опускать необязательные параметры
  • Валидация — можно проверять данные на каждом шаге или в build()
  • Цепочка вызовов — fluent interface делает код приятнее
  • Слабая связанность — изменения в объекте не влияют на клиентский код

Когда использовать

  • Объекты с множеством параметров (более 4-5)
  • Сложные объекты с взаимозависимыми параметрами
  • Когда нужна валидация при конструировании
  • Когда нужны разные конфигурации одного объекта

Builder паттерн — это мощный инструмент для создания сложных объектов в понятной и гибкой форме.

Что такое паттерн Строитель? | PrepBro