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

Какие знаешь порождающие паттерны?

2.0 Middle🔥 141 комментариев
#Архитектура Flutter#ООП и паттерны

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

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

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

Порождающие (Creational) паттерны в Dart/Flutter

Порождающие паттерны занимаются созданием объектов. Они отделяют процесс создания от самого объекта, делая систему независимой от способа создания и состава объектов.

1. Singleton (Одиночка)

Обеспечивает, что у класса только один экземпляр, и предоставляет глобальную точку доступа к нему.

// ❌ Наивная реализация
class AppConfig {
  static AppConfig? _instance;
  
  AppConfig._private();
  
  factory AppConfig() {
    _instance ??= AppConfig._private();
    return _instance!;
  }
  
  String apiUrl = 'https://api.example.com';
}

// ✅ Лучше — через const
class AppConfig {
  static const AppConfig _instance = AppConfig._();
  
  const AppConfig._();
  
  factory AppConfig() {
    return _instance;
  }
  
  final String apiUrl = 'https://api.example.com';
}

// Использование
final config = AppConfig();
final config2 = AppConfig();
print(identical(config, config2)); // true

Применение: логирование, конфигурация, подключение к БД, кеш.

2. Factory (Фабрика)

Предоставляет интерфейс для создания объектов, но оставляет подклассам решение о том, какой класс инстанцировать.

// Абстрактный класс
abstract class Animal {
  String sound();
  
  // Factory конструктор
  factory Animal(String type) {
    switch (type) {
      case 'dog':
        return Dog();
      case 'cat':
        return Cat();
      case 'bird':
        return Bird();
      default:
        throw ArgumentError('Unknown animal type: $type');
    }
  }
}

class Dog implements Animal {
  @override
  String sound() => 'Woof!';
}

class Cat implements Animal {
  @override
  String sound() => 'Meow!';
}

class Bird implements Animal {
  @override
  String sound() => 'Tweet!';
}

// Использование
final dog = Animal('dog');
final cat = Animal('cat');

print(dog.sound()); // Woof!
print(cat.sound()); // Meow!

Применение: создание разных видов объектов с одного интерфейса.

3. Abstract Factory (Абстрактная фабрика)

Предоставляет интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.

// Абстрактная фабрика
abstract class UIFactory {
  Button createButton();
  TextField createTextField();
}

// Компоненты для iOS
class IOSButton implements Button {
  @override
  void render() => print('Rendering iOS button');
}

class IOSTextField implements TextField {
  @override
  void render() => print('Rendering iOS text field');
}

class IOSFactory implements UIFactory {
  @override
  Button createButton() => IOSButton();
  
  @override
  TextField createTextField() => IOSTextField();
}

// Компоненты для Android
class AndroidButton implements Button {
  @override
  void render() => print('Rendering Android button');
}

class AndroidTextField implements TextField {
  @override
  void render() => print('Rendering Android text field');
}

class AndroidFactory implements UIFactory {
  @override
  Button createButton() => AndroidButton();
  
  @override
  TextField createTextField() => AndroidTextField();
}

// Использование
void createUI(UIFactory factory) {
  final button = factory.createButton();
  final textField = factory.createTextField();
  
  button.render();
  textField.render();
}

final isAndroid = true;
final factory = isAndroid ? AndroidFactory() : IOSFactory();
createUI(factory);

Применение: создание тем (светлая/тёмная), платформо-зависимые компоненты.

4. Builder (Строитель)

Отделяет конструирование сложного объекта от его представления, позволяя пошагово конструировать объект.

class User {
  final String id;
  final String name;
  final String? email;
  final String? phone;
  final int? age;
  final String? address;
  
  // Приватный конструктор
  const User._(
    this.id,
    this.name,
    this.email,
    this.phone,
    this.age,
    this.address,
  );
}

// Builder
class UserBuilder {
  late String _id;
  late String _name;
  String? _email;
  String? _phone;
  int? _age;
  String? _address;
  
  UserBuilder(String id, String name) {
    _id = id;
    _name = name;
  }
  
  UserBuilder setEmail(String email) {
    _email = email;
    return this;
  }
  
  UserBuilder setPhone(String phone) {
    _phone = phone;
    return this;
  }
  
  UserBuilder setAge(int age) {
    _age = age;
    return this;
  }
  
  UserBuilder setAddress(String address) {
    _address = address;
    return this;
  }
  
  User build() {
    return User._(
      _id,
      _name,
      _email,
      _phone,
      _age,
      _address,
    );
  }
}

// Использование
final user = UserBuilder('1', 'John')
    .setEmail('john@example.com')
    .setAge(30)
    .setAddress('123 Main St')
    .build();

print(user.name); // John
print(user.email); // john@example.com

Лучше — использовать freezed:

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    String? email,
    String? phone,
    int? age,
    String? address,
  }) = _User;
}

// Использование
final user = User(
  id: '1',
  name: 'John',
  email: 'john@example.com',
  age: 30,
);

// copyWith для создания изменённой копии
final updatedUser = user.copyWith(age: 31);

Применение: конструирование сложных объектов, конфигурация.

5. Prototype (Прототип)

Предоставляет возможность копировать объект без знания его точного класса.

abstract class Prototype {
  Prototype clone();
}

class User implements Prototype {
  String id;
  String name;
  String email;
  
  User(this.id, this.name, this.email);
  
  @override
  User clone() {
    return User(id, name, email);
  }
}

// Использование
final user1 = User('1', 'John', 'john@example.com');
final user2 = user1.clone(); // Копия user1

user2.name = 'Jane'; // Изменение копии не влияет на оригинал

print(user1.name); // John
print(user2.name); // Jane
print(identical(user1, user2)); // false

Лучше — использовать copyWith:

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    required String email,
  }) = _User;
}

final user1 = User(id: '1', name: 'John', email: 'john@example.com');
final user2 = user1.copyWith(name: 'Jane'); // Копия с изменениями

Применение: глубокое копирование объектов, undo/redo операции.

Сравнительная таблица

ПаттернЦельКогда использовать
SingletonОдин экземплярКонфиг, логирование, кеш
FactoryСоздание разных объектов одного интерфейсаСоздание животных, транспорта
Abstract FactoryСоздание семейств объектовТемы UI, платформо-зависимые компоненты
BuilderКонструирование сложных объектовКонфигурация с множеством параметров
PrototypeКопирование объектовUndo/redo, временные копии

Практический пример: HTTP клиент

// Singleton для HTTP клиента
class ApiClient {
  static final ApiClient _instance = ApiClient._();
  
  late final http.Client _httpClient;
  
  ApiClient._() {
    _httpClient = http.Client();
  }
  
  factory ApiClient() {
    return _instance;
  }
  
  // Factory метод для разных типов запросов
  ApiRequest createRequest(String endpoint) {
    return ApiRequest._(_httpClient, endpoint);
  }
}

class ApiRequest {
  final http.Client httpClient;
  final String endpoint;
  Map<String, String> _headers = {};
  
  ApiRequest._(this.httpClient, this.endpoint);
  
  // Builder паттерн
  ApiRequest addHeader(String key, String value) {
    _headers[key] = value;
    return this;
  }
  
  Future<Response> get() async {
    return await httpClient.get(
      Uri.parse(endpoint),
      headers: _headers,
    );
  }
}

// Использование
final apiClient = ApiClient();
final response = await apiClient
    .createRequest('https://api.example.com/users')
    .addHeader('Authorization', 'Bearer token')
    .addHeader('Content-Type', 'application/json')
    .get();

Итоги

Порождающие паттерны помогают:

  • ✅ Упростить создание объектов
  • ✅ Сделать код более гибким
  • ✅ Избежать дублирования логики создания
  • ✅ Облегчить тестирование через dependency injection

В современном Dart коде часто используют factory конструкторы и freezed вместо классических паттернов Builder и Prototype.

Какие знаешь порождающие паттерны? | PrepBro