Pattern Repository
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 100 di 110.
Il pattern Repository separa la logica di accesso ai dati dalla logica di business creando un livello di astrazione tra la tua applicazione e le fonti dati. Invece di far interagire direttamente le tue classi con database, API o file, queste lavorano con un repository che gestisce tutte le operazioni sui dati.
Il modello definisce un'interfaccia per le operazioni sui dati e le implementazioni concrete gestiscono il meccanismo di archiviazione effettivo. Questo consente di sostituire facilmente le fonti di dati senza modificare la logica aziendale:
// Entity
class User {
final int id;
final String name;
User(this.id, this.name);
}
// Repository interface
abstract class UserRepository {
User? getById(int id);
List<User> getAll();
void save(User user);
void delete(int id);
}
// In-memory implementation
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
}La tua logica di business dipende solo dall'interfaccia UserRepository. In seguito potresti creare un DatabaseUserRepository o un ApiUserRepository che implementa la stessa interfaccia e sostituire le implementazioni senza modificare alcun codice che utilizza il repository. Questo schema è essenziale per applicazioni testabili e manutenibili, in cui le origini dei dati possono cambiare nel tempo.
Sfida
FacileCostruiamo un sistema di inventario dei prodotti usando il pattern Repository! Creerai un livello di astrazione che separa il modo in cui i prodotti vengono archiviati da quello in cui vengono utilizzati, rendendo semplice sostituire le implementazioni di archiviazione senza modificare la logica aziendale.
Organizzerai il codice in due file:
product_repository.dart: questo file contiene le classi entità e repository. Inizia con una classeProductche ha campifinalperid(int),name(String) eprice(double). Poi definisci una classe astrattaProductRepositoryche dichiara il contratto per le operazioni sui dati:getById(int id), che restituisce unProductnullable,getAll(), che restituisce una lista di prodotti,save(Product product)per archiviare un prodotto edelete(int id)per rimuoverne uno. Infine, crea unInMemoryProductRepositoryche implementa questa interfaccia usando unMap<int, Product>per l'archiviazione.main.dart: importa il file del repository e mostra il pattern Repository in azione. Crea unInMemoryProductRepository, ma assegnalo a una variabile di tipoProductRepository: questo mostra come il codice dipenda dall'interfaccia e non dall'implementazione. Salva tre prodotti:Laptop(id: 1, prezzo: 999.99),Mouse(id: 2, prezzo: 29.99) eKeyboard(id: 3, prezzo: 79.99). Poi recupera e stampa il prodotto con id 2 nel formatoFound: [name] - $[price]. Elimina il prodotto con id 1. Infine, stampa il numero di prodotti rimanenti comeProducts in stock: [count].
Il vantaggio di questo pattern è che il codice principale conosce solo l'interfaccia ProductRepository. In seguito potresti creare un DatabaseProductRepository o un ApiProductRepository e sostituirlo senza modificare la logica aziendale!
Output previsto:
Found: Mouse - $29.99
Products in stock: 2Provalo tu
import 'product_repository.dart';
void main() {
// Crea il repository: nota che il tipo è l'interfaccia, non l'implementazione
// TODO: Crea un InMemoryProductRepository e memorizzalo in una variabile ProductRepository
// TODO: Salva tre prodotti:
// - Laptop (id: 1, price: 999.99)
// - Mouse (id: 2, price: 29.99)
// - Keyboard (id: 3, price: 79.99)
// TODO: Recupera il prodotto con id 2 e stampa: "Found: [name] - $[price]"
// TODO: Elimina il prodotto con id 1
// TODO: Stampa il numero di prodotti rimanenti: "Products in stock: [count]"
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniLibrerie e importazioniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave thisMetodiVariabili di istanzaFondamenti dei costruttoriRiepilogo - Calcolatrice semplice4Sicurezza dei valori null
Introduzione alla sicurezza dei valori nullNullable e non nullableGli operatori ? e !La keyword late e la sicurezza dei valori nullOperatori null-awareSicurezza dei valori null nelle classiRiepilogo - Sistema di profili utente7Classi astratte e interfacce
Classi astratteMetodi astrattiInterfacce in DartInterfacce impliciteImplementare vs estendereInterfacce multipleRiepilogo - Calcolatore di forme10Collezioni e generici
Panoramica di List, Set e MapCollezioni sicure rispetto ai tipiClassi genericheMetodi genericiVincoli sui genericiIterable e IteratorRiepilogo - Archiviazione generica13Concetti avanzati di OOP
Composizione vs ereditarietàMetodi di estensioneClassi chiamabiliClassi sealed (Dart 3)Record (Dart 3)Pattern e matching (3.0)Enum con metodi16Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente2I costruttori in Dart
Costruttore predefinitoCostruttori con nomeListe di inizializzazioneCostruttori costantiCostruttori factoryCostruttori di reindirizzamentoRiepilogo - Crea forme5Incapsulamento
Membri pubblici e privatiConvenzione del prefisso _Visibilità a livello di libreriaApprofondimento su getter e setterNascondimento delle informazioniRiepilogo - Registri degli studenti8Mixin
Introduzione ai MixinCreare MixinUsare più MixinParola chiave on nei MixinMixin vs ereditarietàMixin vs interfacciaRiepilogo - Sistema animale11Metodi speciali
Sovrascrittura di toString()Sovrascrittura di hashCode e ==Interfaccia ComparableMetodo call()Sovrascrittura di noSuchMethodRiepilogo - Collezione personalizzata14Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneSingletonFactoryObserverStrategy3Proprietà delle classi
Membri di istanza e staticiCampi final e constVariabili lateMetodi e campi staticiGetter e setterRiepilogo - Gestore di conti bancari6Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiL’annotazione @overrideLa parola chiave final per le classiCostruttori ed ereditarietàRiepilogo - Gerarchia dei dipendenti9Polimorfismo
Fondamenti di polimorfismoPolimorfismo tramite interfacceControllo del tipo a runtimeGli operatori is e asParola chiave covariantRiepilogo - Elaboratore di pagamenti12OOP asincrona
Future e async/awaitNozioni di base sugli streamController degli streamCostruttori asincroniAsync nei metodi di classeRiepilogo - Recupero dei dati15Pattern di progettazione Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern RepositoryEsercitati da solo: Compilatore Dart online