リポジトリパターン
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インターフェースのみに依存します。後から、同じインターフェースを実装するDatabaseUserRepositoryやApiUserRepositoryを作成し、リポジトリを使うコードを変更せずに実装を切り替えることができます。このパターンは、データソースが時間とともに変わる可能性のある、テストしやすく保守しやすいアプリケーションに欠かせません。
チャレンジ
簡単リポジトリパターンを使って、商品在庫管理システムを作りましょう!商品の保存方法と使用方法を分離する抽象化レイヤーを作成します。これにより、ビジネスロジックを変更せずに、ストレージの実装を簡単に切り替えられます。
コードを2つのファイルに分けます。
product_repository.dart: このファイルには、エンティティクラスとリポジトリクラスを記述します。まず、id(int)、name(String)、price(double)のfinalフィールドを持つProductクラスを作成します。次に、データ操作の契約を宣言する抽象クラスProductRepositoryを定義します。契約には、null許容のProductを返すgetById(int id)、商品のリストを返すgetAll()、商品を保存するsave(Product product)、商品を削除するdelete(int id)があります。最後に、Map<int, Product>をストレージとして使い、このインターフェースを実装するInMemoryProductRepositoryを作成します。main.dart: リポジトリファイルをインポートし、リポジトリパターンの動作を示します。InMemoryProductRepositoryを作成しますが、ProductRepository型の変数に格納します。これにより、コードが実装ではなくインターフェースに依存していることを示せます。3つの商品を保存します。Laptop(id: 1、価格: 999.99)、Mouse(id: 2、価格: 29.99)、Keyboard(id: 3、価格: 79.99)です。次に、idが2の商品を取得し、Found: [name] - $[price]の形式で出力します。idが1の商品を削除します。最後に、残りの商品の数をProducts in stock: [count]として出力します。
このパターンの利点は、メインコードがProductRepositoryインターフェースだけを認識していればよいことです。後からDatabaseProductRepositoryやApiProductRepositoryを作成し、ビジネスロジックを変更せずに差し替えることができます。
期待される出力:
Found: Mouse - $29.99
Products in stock: 2自分で試してみよう
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]"の形式で出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ自分で練習してみよう: Dartオンラインコンパイラ