Wzorzec repozytorium
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 100 z 110.
Wzorzec repozytorium oddziela logikę dostępu do danych od logiki biznesowej, tworząc warstwę abstrakcji między aplikacją a źródłami danych. Zamiast bezpośrednio komunikować się z bazami danych, interfejsami API lub plikami, klasy współpracują z repozytorium, które obsługuje wszystkie operacje na danych.
Wzorzec definiuje interfejs operacji na danych, a konkretne implementacje obsługują rzeczywisty mechanizm przechowywania. Dzięki temu można łatwo zmieniać źródła danych bez modyfikowania logiki biznesowej:
// Encja
class User {
final int id;
final String name;
User(this.id, this.name);
}
// Interfejs repozytorium
abstract class UserRepository {
User? getById(int id);
List<User> getAll();
void save(User user);
void delete(int id);
}
// Implementacja w pamięci
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
}Twoja logika biznesowa zależy wyłącznie od interfejsu UserRepository. Później możesz utworzyć DatabaseUserRepository lub ApiUserRepository, które implementują ten sam interfejs, i zamienić implementacje bez modyfikowania żadnego kodu korzystającego z repozytorium. Ten wzorzec jest niezbędny w aplikacjach, które można testować i łatwo utrzymywać, a których źródła danych mogą się zmieniać z upływem czasu.
Wyzwanie
ŁatwyZbudujmy system ewidencji produktów z użyciem wzorca Repository! Utworzysz warstwę abstrakcji, która oddzieli sposób przechowywania produktów od sposobu ich używania, dzięki czemu będzie można łatwo zamieniać implementacje magazynowania bez zmieniania logiki biznesowej.
Podzielisz swój kod na dwa pliki:
product_repository.dart: Ten plik zawiera klasy encji i repozytorium. Zacznij od klasyProductz polamifinaldlaid(int),name(String) iprice(double). Następnie zdefiniuj klasę abstrakcyjnąProductRepository, która deklaruje kontrakt operacji na danych:getById(int id)zwracającą wartośćProductdopuszczającą null,getAll()zwracającą listę produktów,save(Product product)do przechowywania produktu orazdelete(int id)do usuwania produktu. Na koniec utwórzInMemoryProductRepository, która implementuje ten interfejs, używającMap<int, Product>do przechowywania danych.main.dart: Zaimportuj plik repozytorium i pokaż wzorzec Repository w działaniu. UtwórzInMemoryProductRepository, ale przechowaj ją w zmiennej typuProductRepository— dzięki temu pokażesz, że kod zależy od interfejsu, a nie od implementacji. Zapisz trzy produkty:Laptop(id: 1, price: 999.99),Mouse(id: 2, price: 29.99) iKeyboard(id: 3, price: 79.99). Następnie pobierz i wyświetl produkt o id 2 w formacieFound: [name] - $[price]. Usuń produkt o id 1. Na koniec wyświetl liczbę pozostałych produktów jakoProducts in stock: [count].
Siła tego wzorca polega na tym, że główny kod zna tylko interfejs ProductRepository. Później możesz utworzyć DatabaseProductRepository lub ApiProductRepository i podmienić repozytorium bez zmieniania logiki biznesowej!
Oczekiwany wynik:
Found: Mouse - $29.99
Products in stock: 2Spróbuj swoich sił
import 'product_repository.dart';
void main() {
// Utwórz repozytorium — zwróć uwagę, że typem jest interfejs, a nie implementacja
// TODO: Utwórz InMemoryProductRepository i przypisz je do zmiennej typu ProductRepository
// TODO: Zapisz trzy produkty:
// - Laptop (id: 1, price: 999.99)
// - Mouse (id: 2, price: 29.99)
// - Keyboard (id: 3, price: 79.99)
// TODO: Pobierz produkt o id 2 i wypisz: "Found: [name] - $[price]"
// TODO: Usuń produkt o id 1
// TODO: Wypisz liczbę pozostałych produktów: "Products in stock: [count]"
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatności12Asynchroniczne OOP
Futures i async/awaitPodstawy strumieniKontrolery strumieniKonstruktory asynchroniczneAsynchroniczność w metodach klasyPodsumowanie — pobieranie danych15Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec repozytoriumPoćwicz samodzielnie: Kompilator Dart online