Padrão Repository
Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy. Lição 100 de 110.
O padrão Repository separa a lógica de acesso a dados da lógica de negócios, criando uma camada de abstração entre sua aplicação e as fontes de dados. Em vez de suas classes interagirem diretamente com bancos de dados, APIs ou arquivos, elas trabalham com um repositório que lida com todas as operações de dados.
O padrão define uma interface para operações de dados, e as implementações concretas lidam com o mecanismo de armazenamento real. Isso facilita a troca das fontes de dados sem alterar sua lógica de negócios:
// Entidade
class User {
final int id;
final String name;
User(this.id, this.name);
}
// Interface do repositório
abstract class UserRepository {
User? getById(int id);
List<User> getAll();
void save(User user);
void delete(int id);
}
// Implementação em memória
class InMemoryUserRepository implements UserRepository {
final Map<int, User> _users = {};
@override
User? getById(int id) => _users[id];
@override
List<User> getAll() => _users.values.toList();
@override
void save(User user) => _users[user.id] = user;
@override
void delete(int id) => _users.remove(id);
}
void main() {
UserRepository repo = InMemoryUserRepository();
repo.save(User(1, 'Alice'));
repo.save(User(2, 'Bob'));
print(repo.getById(1)?.name); // Alice
print(repo.getAll().length); // 2
}Sua lógica de negócio depende apenas da interface UserRepository. Mais tarde, você poderia criar um DatabaseUserRepository ou um ApiUserRepository que implemente a mesma interface e trocar as implementações sem modificar nenhum código que use o repositório. Esse padrão é essencial para aplicações testáveis e fáceis de manter, nas quais as fontes de dados podem mudar ao longo do tempo.
Desafio
FácilVamos criar um sistema de inventário de produtos usando o padrão Repository! Você criará uma camada de abstração que separa a forma como os produtos são armazenados da forma como são usados, facilitando a troca das implementações de armazenamento sem alterar a lógica de negócio.
Você organizará seu código em dois arquivos:
product_repository.dart: Este arquivo contém suas entidades e classes de repositório. Comece com uma classeProductque tenha camposfinalparaid(int),name(String) eprice(double). Em seguida, defina uma classe abstrataProductRepositoryque declare o contrato para as operações de dados:getById(int id), retornando umProductanulável;getAll(), retornando uma lista de produtos;save(Product product), para armazenar um produto; edelete(int id), para remover um produto. Por fim, crie umInMemoryProductRepositoryque implemente essa interface usando umMap<int, Product>para o armazenamento.main.dart: Importe seu arquivo de repositório e demonstre o padrão Repository em ação. Crie umInMemoryProductRepository, mas armazene-o em uma variável do tipoProductRepository— isso mostra como seu código depende da interface, não da implementação. Salve três produtos:Laptop(id: 1, price: 999.99),Mouse(id: 2, price: 29.99) eKeyboard(id: 3, price: 79.99). Em seguida, recupere e imprima o produto com id 2 no formatoFound: [name] - $[price]. Exclua o produto com id 1. Por fim, imprima a quantidade de produtos restantes comoProducts in stock: [count].
O poder desse padrão é que seu código principal conhece apenas a interface ProductRepository. Posteriormente, você poderia criar um DatabaseProductRepository ou um ApiProductRepository e substituí-lo sem alterar nenhuma parte da lógica de negócio!
Saída esperada:
Found: Mouse - $29.99
Products in stock: 2Experimente você mesmo
import 'product_repository.dart';
void main() {
// Criar repositório - note que o tipo é a interface, não a implementação
// TODO: Criar um InMemoryProductRepository e armazená-lo em uma variável ProductRepository
// TODO: Salvar três produtos:
// - Laptop (id: 1, price: 999.99)
// - Mouse (id: 2, price: 29.99)
// - Keyboard (id: 3, price: 79.99)
// TODO: Recuperar o produto com id 2 e imprimir: "Found: [name] - $[price]"
// TODO: Excluir o produto com id 1
// TODO: Imprimir a contagem de produtos restantes: "Products in stock: [count]"
}
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 OOP
Arquivos externosBibliotecas e importsIntrodução à OOPClasses vs. objetosA palavra-chave thisMétodosVariáveis de instânciaNoções básicas de construtoresRevisão - Calculadora simples4Segurança contra nulos
Introdução à segurança contra nulosAnulável vs. não anulávelOs operadores ? e !Palavra-chave Late e segurança contra nulosOperadores conscientes de nulosSegurança contra nulos em classesRecapitulação — Sistema de perfil de usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementação vs. ExtensãoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Genéricos
Visão geral de List, Set e MapColeções com segurança de tiposClasses genéricasMétodos genéricosRestrições genéricasIterable e IteratorRecapitulação - Armazenamento genérico13Conceitos Avançados de OOP
Composição vs. HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Registros (Dart 3)Padrões e Correspondência (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses de Livros e Usuários2Construtores em Dart
Construtor padrãoConstrutores nomeadosListas de inicializaçãoConstrutores constantesConstrutores factoryConstrutores de redirecionamentoRevisão - Construtor de formas5Encapsulamento
Membros Públicos vs. PrivadosConvenção do Prefixo _Privacidade no Nível da BibliotecaAprofundamento em Getters e SettersOcultação de InformaçõesRecapitulação - Registros de Alunos8Mixins
Introdução a MixinsCriando MixinsUsando vários MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema de Animais11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode e ==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 e ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Contas Bancárias6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodosA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do polimorfismoPolimorfismo por meio de interfacesVerificação de tipos em tempo de execuçãoOs operadores is e asPalavra-chave covarianteRecapitulação — Processador de pagamentos12OOP Assíncrona
Futures e async/awaitNoções básicas de StreamsControladores de StreamConstrutores assíncronosAsync em métodos de classeRecapitulação — Buscador de dados15Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão RepositoryPratique por conta própria: Compilador de Dart online