Swiftのリポジトリデザインパターン

モデルを照会するクリーンな方法

どのような問題を解決しますか?

コード内のさまざまな場所からモデルオブジェクトを何度もクエリする必要がある場合、リポジトリを使用すると、モデルを操作して重複するクエリコードを削除するための単一エントリポイントを提供できます。それをさらに進めてプロトコルで使用することができます。これにより、実装を簡単に切り替えることができます(たとえば、単体テスト用)、またはジェネリックで使用してより多くの*ドラムロール*ジェネリックな抽象化を行うことができます。この記事では、これらすべてのケースを取り上げます。

シーンのスケッチ。

APIからデータを取得し、これをモデルオブジェクトにマッピングするコードがあるとします。この例では、サーバーから記事のリストを取得します。

これは少しファンキーに見えるかもしれませんが、Moyaをネットワーク抽象化レイヤーとして使用するRxSwiftにすぎませんが、実際に何が起こっているかを理解することは重要ではありません。データの取得方法は完全にあなた次第です。

このコードは

  1. サーバーへのGETリクエスト
  2. 返されたJSONをArticleオブジェクトの配列にマップします
  3. すべての作業が完了すると、クロージャーが呼び出されます。

リポジトリが必要なのはなぜですか?

現時点ではありません。コードベース全体でAPIを1回だけ呼び出すと、リポジトリの追加が過剰になる場合があります(または一部のエンジニアが過剰エンジニアリングと言う場合があります)。

わかりました...しかし、いつリポジトリオブジェクトを使用するのが便利ですか?
コードベースが成長し始め、記事を何度も取得するコードを記述する必要があるとします。 「すべての記事を取得する必要がある場所にコードをコピーして貼り付けましょう」と言うかもしれません。

害はなく、誰も死ななかった。右?

その瞬間、大きな赤いアラームが脳内で点滅し始めます。

こんにちはリポジトリ。

リポジトリは、モデルを1か所でクエリするためのすべてのコードをカプセル化する単なるオブジェクトです。したがって、たとえば、すべての記事を取得します。

記事を取得するためのパブリックAPIを提供するリポジトリオブジェクトを作成しましょう。

これで、このメソッドを呼び出すことができ、実際の記事を取得するために舞台裏で何が起こるか心配する必要がなくなりました。
メソッドを呼び出すだけで、記事を取得できます。いいですね
しかし、待って、もっとあります!

すべての記事のやり取りを処理する

リポジトリを使用して、モデルオブジェクトとやり取りするメソッドを追加できます。ほとんどの場合、モデルに対してCRUD(作成、読み取り、更新、削除)操作を行います。リポジトリにこれらの操作のロジックを追加するだけです。

これにより、同じコードを何度も繰り返すことなく、コード全体で使用できる優れたAPIが作成されます。

実際には、リポジトリの使用は次のようになります。

とても読みやすく、読みやすいでしょう?しかし、それがさらに良くなるのを待ってください。

パワーアップ:プロトコル

前のコードでは、常に「APIからデータを取得する」例を使用しました。しかし、オンラインソースではなくローカルJSONファイルからデータをロードするサポートを追加する必要がある場合はどうでしょう。

メソッド名をリストするプロトコルを作成する場合は、オンラインAPIの実装とデータをオフラインにするための実装を作成できます。

これは次のようになります。

プロトコルには、「私に準拠する場合、これらのメソッドの署名が必要ですが、実際の実装は気にしません!」

そのため、WebArticleRepositoryとLocalArticleRepositoryを作成できます。どちらもプロトコルにリストされているすべてのメソッドを備えていますが、まったく異なる2つの実装を作成できます。

パワーアップ:ユニットテスト

プロトコルの使用は、コードを単体テストする場合にも非常に便利です。リポジトリプロトコルを実装する別のオブジェクトを作成するだけで、代わりにモックデータを返すことができるからです。

これを依存性注入と併用すると、特定のオブジェクトを簡単にテストできます。

ビューモデルがあり、ビューモデルがリポジトリ経由でデータを取得するとします。

ビューモデルをテストする場合、Webから取得される記事にこだわっています。
これは実際に私たちが望むものではありません。私たちは、テストを可能な限り決定論的にしたいと考えています。この場合、Webから取得した記事は時間とともに変化する可能性があり、テストの実行時にインターネットに接続できず、サーバーがダウンする可能性があります。これらはすべて、テストが失敗するシナリオです制御不能です。そして、テストするとき、私たちはコントロールすることを望んでいます。

幸いなことに、実際にこれを解決するのは本当に簡単です。

こんにちは、依存性注入。

初期化子を介してarticleRepoプロパティを設定するだけです。デフォルトのケースは、実動コードに必要なものであり、単体テストを作成するときに、リポジトリをモックバージョンと交換できます。

しかし、多分あなたは考えている、よくタイプについてはどうですか? WebArticleRepositoryはMockArticleRepositoryではないため、コンパイラーは文句を言いませんか?タイプとしてプロトコルを使用する場合は別です。このようにして、コンパイラに通知し、ArticleRepositoryプロトコル(WebとMockArticleRepositoryの両方が行うプロトコル)に準拠している限り、すべてを許可します。

最終的なコードは次のようになります。

そして、単体テストでは、このように交換できます。

これで、リポジトリが返すデータを完全に制御できます。

スーパーパワーアップ:ジェネリック

ジェネリックを使用することで、これをさらに進めることができます。考えてみると、ほとんどのリポジトリは常に同じ操作をしています

  1. すべてを手に入れる
  2. いくつかのものを手に入れる
  3. いくつかのものを挿入する
  4. 物を削除する
  5. 更新する

違いがあるのは「もの」という言葉だけなので、これはジェネリックのプロトコルを使用するための優れた候補かもしれません。複雑に聞こえるかもしれませんが、実際には非常に簡単です。

最初に、プロトコルの名前をRepositoryに変更し、より汎用的にします。
そして、すべての記事タイプを削除し、それらを魔法のTに置き換えます。しかし、文字Tは単なる…の置き換えになります。プロトコルの関連タイプとしてTをマークする必要があります。

これで、このプロトコルを任意のモデルオブジェクトに使用できます。

1.記事リポジトリ

メソッドを実装することでTが何であるかを指定したため、コンパイラはTのタイプをArticleに推測します。この場合、Articleオブジェクト。

2.ユーザーリポジトリ

それでおしまい。

記事をお楽しみいただき、ご質問やご意見がありましたら、以下に質問するか、Twitterで私に連絡してチャットしましょう。