Construtores Assíncronos
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 79 de 110.
Construtores em Dart não podem ser marcados como async - eles devem retornar uma instância da classe imediatamente, não um Future. Então, como você cria objetos que precisam de inicialização assíncrona, como carregar dados de um banco de dados ou buscar configurações?
A solução é usar um método factory assíncrono estático. Mantenha o construtor privado (ou simples) e forneça um método estático que realize o trabalho assíncrono e retorne um Future da sua classe:
class UserProfile {
final String name;
final int age;
UserProfile._(this.name, this.age); // Construtor privado
static Future<UserProfile> create(String userId) async {
// Simula a busca de dados
await Future.delayed(Duration(seconds: 1));
return UserProfile._('Alice', 25);
}
}
Future<void> main() async {
var user = await UserProfile.create('123');
print(user.name); // Alice
}O construtor privado UserProfile._ lida com a criação real do objeto, enquanto o método estático create() lida com todo o trabalho assíncrono. Os chamadores usam await para obter o objeto totalmente inicializado.
Este padrão garante que seu objeto esteja completamente pronto para uso assim que você o tiver — sem estados parcialmente inicializados ou dados ausentes. É comumente usado para classes que precisam carregar recursos, conectar-se a serviços ou realizar qualquer configuração que leve tempo.
Desafio
FácilVamos construir um carregador de configuração que demonstra como lidar com a inicialização assíncrona em classes! Como os construtores não podem ser assíncronos, você usará o padrão de método de fábrica estático (static factory method) para criar objetos que precisam de tempo para "carregar" seus dados.
Você organizará seu código em dois arquivos:
config_loader.dart: Crie uma classeConfigLoaderque simula o carregamento de configurações de forma assíncrona. Sua classe deve ter:- Três campos finais:
appName(String),version(String) emaxUsers(int) - Um construtor privado que inicializa todos os três campos
- Um método de fábrica estático assíncrono chamado
loadque recebe um parâmetroString configIde retorna umFuture<ConfigLoader>. Este método deve:- Usar
Future.delayed()com 100 milissegundos para simular o tempo de carregamento - Gerar os valores de configuração baseados no
configId:appName: o configId com" App"anexadoversion:"1.0."seguido pelo comprimento do configIdmaxUsers: o comprimento do configId multiplicado por 10
- Retornar uma nova instância de
ConfigLoadercom esses valores
- Usar
- Um método
displayInfo()que imprime a configuração neste formato:Config: [appName] v[version] (max [maxUsers] users)
- Três campos finais:
main.dart: Importe seu carregador de configuração e demonstre o padrão de inicialização assíncrona:- Imprima
Loading configurations... - Carregue uma configuração com o configId
"Production"e chame seudisplayInfo() - Carregue uma configuração com o configId
"Dev"e chame seudisplayInfo() - Imprima
All configs loaded!
- Imprima
Observe como o método estático load lida com todo o trabalho assíncrono, enquanto o construtor privado simplesmente atribui os valores. Os chamadores usam await para obter um objeto totalmente inicializado!
Saída esperada:
Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!Folha de consulta
Construtores em Dart não podem ser marcados como async porque eles devem retornar uma instância da classe imediatamente, não um Future.
Para criar objetos que precisam de inicialização assíncrona, use um método factory estático assíncrono:
class UserProfile {
final String name;
final int age;
UserProfile._(this.name, this.age); // Construtor privado
static Future<UserProfile> create(String userId) async {
// Realize o trabalho assíncrono aqui
await Future.delayed(Duration(seconds: 1));
return UserProfile._('Alice', 25);
}
}Uso:
Future<void> main() async {
var user = await UserProfile.create('123');
print(user.name); // Alice
}O construtor privado (marcado com _) lida com a criação do objeto, enquanto o método estático lida com o trabalho assíncrono. Isso garante que os objetos estejam totalmente inicializados antes do uso.
Experimente você mesmo
import 'config_loader.dart';
void main() async {
// TODO: Imprima "Loading configurations..."
// TODO: Carregue uma configuração com configId "Production"
// e chame seu método displayInfo()
// TODO: Carregue uma configuração com configId "Dev"
// e chame seu método displayInfo()
// TODO: Imprima "All configs loaded!"
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosBibliotecas e ImportaçõesIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosVariáveis de InstânciaFundamentos de ConstrutoresRecapitulação - Calculadora Simples4Null Safety
Introdução ao Null SafetyNullable vs Non-NullableOs Operadores ? e !Palavra-chave Late e Null SafetyOperadores Null-AwareNull Safety em ClassesRecapitulação - Sistema de Perfil de Usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementando vs EstendendoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Generics
Visão Geral de List, Set e MapColeções Type-SafeClasses GenéricasMétodos GenéricosRestrições de GenericsIterable e IteratorRecapitulação - Armazenamento Genérico13Conceitos Avançados de POO
Composição vs HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário2Construtores em Dart
Construtor PadrãoConstrutores NomeadosListas de InicializaçãoConstrutores ConstantesConstrutores FactoryConstrutores de RedirecionamentoRecapitulação - Shape Builder5Encapsulamento
Membros Públicos vs PrivadosA Convenção do Prefixo _Privacidade em Nível de BibliotecaAprofundamento em Getters & SettersOcultação de InformaçãoRecapitulação - Registros de Estudantes8Mixins
Introdução aos MixinsCriando MixinsUsando Múltiplos MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema Animal11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode & ==Interface ComparableMétodo call()Sobrescrita de noSuchMethodRecapitulação - Coleção Personalizada14Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy3Propriedades de Classe
Membros de Instância vs EstáticosCampos Final & ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Conta Bancária6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodoA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do PolimorfismoPolimorfismo via InterfacesVerificação de Tipos em Tempo de ExecuçãoOs Operadores is & asPalavra-chave CovariantRecapitulação - Processador de Pagamentos12POO Assíncrona
Futures e async/awaitFundamentos de StreamsStream ControllersConstrutores AssíncronosAsync em Métodos de ClasseRecapitulação - Data Fetcher15Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository