Menu
Coddy logo textTech

Асинхронные конструкторы

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 79 из 110.

Конструкторы в Dart не могут быть помечены как async — они должны немедленно возвращать экземпляр класса, а не Future. Так как же создавать объекты, которым требуется асинхронная инициализация, например, загрузка данных из базы данных или получение конфигурации?

Решение состоит в использовании статического асинхронного фабричного метода. Сделайте конструктор приватным (или простым) и предоставьте статический метод, который выполняет асинхронную работу и возвращает Future вашего класса:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Приватный конструктор
  
  static Future<UserProfile> create(String userId) async {
    // Имитация получения данных
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

Приватный конструктор UserProfile._ отвечает за непосредственное создание объекта, в то время как статический метод create() выполняет всю асинхронную работу. Вызывающий код использует await для получения полностью инициализированного объекта.

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

challenge icon

Задание

Легко

Давайте создадим загрузчик конфигурации, который демонстрирует, как обрабатывать асинхронную инициализацию в классах! Поскольку конструкторы не могут быть асинхронными, вы будете использовать паттерн статического фабричного метода для создания объектов, которым требуется время для «загрузки» своих данных.

Вы организуете свой код в два файла:

  • config_loader.dart: Создайте класс ConfigLoader, который имитирует асинхронную загрузку настроек конфигурации. Ваш класс должен иметь:
    • Три финальных поля: appName (String), version (String) и maxUsers (int)
    • Приватный конструктор, который инициализирует все три поля
    • Статический асинхронный фабричный метод с именем load, который принимает параметр String configId и возвращает Future<ConfigLoader>. Этот метод должен:
      • Использовать Future.delayed() со 100 миллисекундами для имитации времени загрузки
      • Генерировать значения конфигурации на основе configId:
        • appName: configId с добавлением " App"
        • version: "1.0.", за которым следует длина configId
        • maxUsers: длина configId, умноженная на 10
      • Возвращать новый экземпляр ConfigLoader с этими значениями
    • Метод displayInfo(), который выводит конфигурацию в следующем формате: Config: [appName] v[version] (max [maxUsers] users)
  • main.dart: Импортируйте ваш загрузчик конфигурации и продемонстрируйте паттерн асинхронной инициализации:
    • Вывести Loading configurations...
    • Загрузить конфигурацию с configId "Production" и вызвать её метод displayInfo()
    • Загрузить конфигурацию с configId "Dev" и вызвать её метод displayInfo()
    • Вывести All configs loaded!

Обратите внимание, как статический метод load обрабатывает всю асинхронную работу, в то время как приватный конструктор просто присваивает значения. Вызывающая сторона использует await для получения полностью инициализированного объекта!

Ожидаемый вывод:

Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!

Шпаргалка

Конструкторы в Dart не могут быть помечены как async, так как они должны немедленно возвращать экземпляр класса, а не Future.

Для создания объектов, требующих асинхронной инициализации, используйте статический асинхронный фабричный метод:

class UserProfile {
  final String name;
  final int age;
  
  UserProfile._(this.name, this.age);  // Приватный конструктор
  
  static Future<UserProfile> create(String userId) async {
    // Выполняем асинхронную работу здесь
    await Future.delayed(Duration(seconds: 1));
    return UserProfile._('Alice', 25);
  }
}

Использование:

Future<void> main() async {
  var user = await UserProfile.create('123');
  print(user.name);  // Alice
}

Приватный конструктор (помеченный _) отвечает за создание объекта, в то время как статический метод выполняет асинхронную работу. Это гарантирует, что объекты будут полностью инициализированы перед использованием.

Попробуйте сами

import 'config_loader.dart';

void main() async {
  // TODO: Выведите "Loading configurations..."
  
  // TODO: Загрузите конфигурацию с configId "Production"
  // и вызовите её метод displayInfo()
  
  // TODO: Загрузите конфигурацию с configId "Dev"
  // и вызовите её метод displayInfo()
  
  // TODO: Выведите "All configs loaded!"
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование