Menu
Coddy logo textTech

Repository-Muster

Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 100 von 110.

Das Repository-Pattern trennt die Datenzugriffslogik von der Geschäftslogik, indem es eine Abstraktionsschicht zwischen Ihrer Anwendung und den Datenquellen erstellt. Anstatt Ihre Klassen direkt mit Datenbanken, APIs oder Dateien interagieren zu lassen, arbeiten sie mit einem Repository, das alle Datenoperationen übernimmt.

Das Muster definiert eine Schnittstelle für Datenoperationen, und konkrete Implementierungen übernehmen den eigentlichen Speichermechanismus. Dies macht es einfach, Datenquellen auszutauschen, ohne Ihre Geschäftslogik zu ändern:

// Entität
class User {
  final int id;
  final String name;
  User(this.id, this.name);
}

// Repository-Schnittstelle
abstract class UserRepository {
  User? getById(int id);
  List<User> getAll();
  void save(User user);
  void delete(int id);
}

// In-Memory-Implementierung
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
}

Ihre Geschäftslogik hängt nur von der Schnittstelle UserRepository ab. Sie könnten später ein DatabaseUserRepository oder ApiUserRepository erstellen, das dieselbe Schnittstelle implementiert, und die Implementierungen austauschen, ohne den Code zu ändern, der das Repository verwendet. Dieses Muster ist essenziell für testbare, wartbare Anwendungen, bei denen sich Datenquellen im Laufe der Zeit ändern können.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Produktinventarsystem mit dem Repository-Muster erstellen! Sie werden eine Abstraktionsschicht erstellen, die die Speicherung von Produkten von deren Verwendung trennt, sodass Speicherimplementierungen einfach ausgetauscht werden können, ohne die Geschäftslogik zu ändern.

Sie werden Ihren Code in zwei Dateien organisieren:

  • product_repository.dart: Diese Datei enthält Ihre Entitäts- und Repository-Klassen. Beginnen Sie mit einer Product-Klasse, die final-Felder für id (int), name (String) und price (double) hat. Definieren Sie dann eine abstrakte Klasse ProductRepository, die den Vertrag für Datenoperationen deklariert: getById(int id) gibt ein nullable Product zurück, getAll() gibt eine Liste von Produkten zurück, save(Product product) zum Speichern eines Produkts und delete(int id) zum Entfernen eines Produkts. Erstellen Sie schließlich ein InMemoryProductRepository, das diese Schnittstelle unter Verwendung einer Map<int, Product> zur Speicherung implementiert.
  • main.dart: Importieren Sie Ihre Repository-Datei und demonstrieren Sie das Repository-Muster in Aktion. Erstellen Sie ein InMemoryProductRepository, aber speichern Sie es in einer Variablen vom Typ ProductRepository – dies zeigt, wie Ihr Code von der Schnittstelle und nicht von der Implementierung abhängt. Speichern Sie drei Produkte: Laptop (id: 1, price: 999.99), Mouse (id: 2, price: 29.99) und Keyboard (id: 3, price: 79.99). Rufen Sie dann das Produkt mit der id 2 ab und geben Sie es im Format Found: [name] - $[price] aus. Löschen Sie das Produkt mit der id 1. Geben Sie schließlich die Anzahl der verbleibenden Produkte als Products in stock: [count] aus.

Die Stärke dieses Musters liegt darin, dass Ihr Hauptcode nur die ProductRepository-Schnittstelle kennt. Sie könnten später ein DatabaseProductRepository oder ApiProductRepository erstellen und es austauschen, ohne die Geschäftslogik zu ändern!

Erwartete Ausgabe:

Found: Mouse - $29.99
Products in stock: 2

Spickzettel

Das Repository-Pattern trennt die Datenzugriffslogik von der Geschäftslogik, indem es eine Abstraktionsschicht zwischen Ihrer Anwendung und den Datenquellen erstellt.

Definieren Sie ein Interface für Datenoperationen:

// Entität
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);
}

Erstellen Sie konkrete Implementierungen, die den tatsächlichen Speichermechanismus verwalten:

// In-Memory-Implementierung
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);
}

Verwenden Sie das Repository über sein Interface:

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
}

Die Geschäftslogik hängt nur vom Interface ab, was es Ihnen ermöglicht, Implementierungen auszutauschen (z. B. DatabaseUserRepository, ApiUserRepository), ohne den Code zu ändern, der das Repository verwendet.

Probier es selbst

import 'product_repository.dart';

void main() {
  // Repository erstellen - beachte, dass der Typ das Interface ist, nicht die Implementierung
  // TODO: Erstelle ein InMemoryProductRepository und speichere es in einer ProductRepository Variable
  
  // TODO: Speichere drei Produkte:
  // - Laptop (id: 1, price: 999.99)
  // - Mouse (id: 2, price: 29.99)
  // - Keyboard (id: 3, price: 79.99)
  
  // TODO: Rufe das Produkt mit id 2 ab und gib es aus: "Found: [name] - $[price]"
  
  // TODO: Lösche das Produkt mit id 1
  
  // TODO: Gib die Anzahl der verbleibenden Produkte aus: "Products in stock: [count]"
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung