Menu
Coddy logo textTech

リポジトリパターン

CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 100/110。

リポジトリパターンは、アプリケーションとデータソースの間に抽象化レイヤーを作成することで、データアクセスロジックをビジネスロジックから分離します。クラスがデータベース、API、またはファイルと直接やり取りする代わりに、すべてのデータ操作を処理するリポジトリを介して動作します。

このパターンはデータ操作のためのインターフェースを定義し、具体的な実装が実際のストレージメカニズムを処理します。これにより、ビジネスロジックを変更することなく、データソースを簡単に切り替えることができます。

// エンティティ
class User {
  final int id;
  final String name;
  User(this.id, this.name);
}

// リポジトリインターフェース
abstract class UserRepository {
  User? getById(int id);
  List<User> getAll();
  void save(User user);
  void delete(int id);
}

// インメモリ実装
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
}

ビジネスロジックは、UserRepositoryインターフェースにのみ依存します。後で、同じインターフェースを実装するDatabaseUserRepositoryApiUserRepositoryを作成し、リポジトリを使用するコードを一切変更することなく実装を入れ替えることができます。このパターンは、データソースが時間の経過とともに変更される可能性がある、テスト可能でメンテナンス性の高いアプリケーションにとって不可欠です。

challenge icon

チャレンジ

簡単

リポジトリパターンを使用して、製品在庫システムを構築しましょう!製品の保存方法と使用方法を分離する抽象化レイヤーを作成することで、ビジネスロジックを変更せずにストレージの実装を簡単に切り替えられるようにします。

コードを2つのファイルに整理します:

  • product_repository.dart: このファイルには、エンティティクラスとリポジトリクラスが含まれます。まず、id (int)、name (String)、price (double) の final フィールドを持つ Product クラスから始めます。次に、データ操作のコントラクトを宣言する抽象クラス ProductRepository を定義します。これには、nullableな Product を返す getById(int id)、製品のリストを返す getAll()、製品を保存する save(Product product)、および製品を削除する delete(int id) が含まれます。最後に、ストレージとして Map<int, Product> を使用してこのインターフェースを実装する InMemoryProductRepository を作成します。
  • main.dart: リポジトリファイルをインポートし、リポジトリパターンの動作を実演します。InMemoryProductRepository を作成しますが、それを ProductRepository 型の変数に格納してください。これにより、コードが実装ではなくインターフェースに依存していることが示されます。3つの製品を保存します:Laptop (id: 1, price: 999.99)、Mouse (id: 2, price: 29.99)、および Keyboard (id: 3, price: 79.99)。次に、id 2 の製品を取得し、Found: [name] - $[price] の形式で出力します。id 1 の製品を削除します。最後に、残りの製品の数を Products in stock: [count] として出力します。

このパターンの強力な点は、メインコードが ProductRepository インターフェースのみを認識していることです。後で DatabaseProductRepositoryApiProductRepository を作成し、ビジネスロジックを一切変更せずにそれらを差し替えることができます!

期待される出力:

Found: Mouse - $29.99
Products in stock: 2

チートシート

リポジトリパターンは、アプリケーションとデータソースの間に抽象化レイヤーを作成することで、データアクセスロジックをビジネスロジックから分離します。

データ操作のためのインターフェースを定義します:

// エンティティ
class User {
  final int id;
  final String name;
  User(this.id, this.name);
}

// リポジトリインターフェース
abstract class UserRepository {
  User? getById(int id);
  List<User> getAll();
  void save(User user);
  void delete(int id);
}

実際のストレージメカニズムを処理する具体的な実装を作成します:

// インメモリ実装
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
}

ビジネスロジックはインターフェースにのみ依存するため、リポジトリを使用するコードを変更することなく、実装(例:DatabaseUserRepositoryApiUserRepository)を入れ替えることができます。

自分で試してみよう

import 'product_repository.dart';

void main() {
  // リポジトリを作成します - 型は実装ではなくインターフェースであることに注意してください
  // TODO: InMemoryProductRepository を作成し、ProductRepository 変数に格納します
  
  // TODO: 3つの製品を保存します:
  // - Laptop (id: 1, price: 999.99)
  // - Mouse (id: 2, price: 29.99)
  // - Keyboard (id: 3, price: 79.99)
  
  // TODO: id が 2 の製品を取得し、"Found: [name] - $[price]" と出力します
  
  // TODO: id が 1 の製品を削除します
  
  // TODO: 残りの製品数を出力します: "Products in stock: [count]"
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン