Асинхронные конструкторы
Часть раздела Объектно-ориентированное программирование путешествия по 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 для получения полностью инициализированного объекта.
Этот паттерн гарантирует, что ваш объект полностью готов к использованию, как только вы его получили — никаких частично инициализированных состояний или отсутствующих данных. Он обычно используется для классов, которым необходимо загружать ресурсы, подключаться к сервисам или выполнять любую настройку, требующую времени.
Задание
ЛегкоДавайте создадим загрузчик конфигурации, который демонстрирует, как обрабатывать асинхронную инициализацию в классах! Поскольку конструкторы не могут быть асинхронными, вы будете использовать паттерн статического фабричного метода для создания объектов, которым требуется время для «загрузки» своих данных.
Вы организуете свой код в два файла:
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.", за которым следует длина configIdmaxUsers: длина 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!"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий