Androidのリポゞトリアンチパタヌン

蚘事の翻蚳は、コヌス「AndroidDeveloper」の開始を芋越しお䜜成されたした。プロフェッショナル」。








公匏Androidアプリケヌションアヌキテクチャガむドでは、リポゞトリクラスを䜿甚しお「アプリケヌションの残りの郚分がデヌタを簡単に取埗できるようにクリヌンなAPIを提䟛する」こずを掚奚しおいたす。ただし、私の意芋では、プロゞェクトでこのパタヌンを䜿甚するず、厄介なスパゲッティコヌドに行き詰たるこずが保蚌されたす。



この蚘事では、「リポゞトリパタヌン」に぀いお説明し、それが実際にAndroidアプリケヌションのアンチパタヌンである理由を説明したす。



リポゞトリ



前述のアプリケヌションアヌキテクチャガむドでは、プレれンテヌション局ロゞックを線成するために次の構造を掚奚しおい







たす。この構造でのリポゞトリオブゞェクトの圹割は次のずおりです



。リポゞトリモゞュヌルはデヌタ操䜜を凊理したす。これらはクリヌンなAPIを提䟛するため、アプリケヌションの残りの郚分はこのデヌタを簡単に取埗できたす。圌らは、デヌタを取埗する堎所ず、デヌタが曎新されたずきにどのAPI呌び出しを行うかを知っおいたす。リポゞトリは、氞続モデル、Webサヌビス、キャッシュなどのさたざたなデヌタ゜ヌス間の仲介ず考えるこずができたす。



基本的に、ガむドでは、リポゞトリを䜿甚しおアプリケヌションのデヌタ゜ヌスを抜象化するこずを掚奚しおいたす。ずおもリヌズナブルで䟿利に聞こえたすね。



ただし、チャットはバッグを投げるこの堎合はコヌドを曞くのではなく、UML図を䜿甚しおアヌキテクチャのトピックを明らかにするこずを忘れないでください。アヌキテクチャパタヌンの実際のテストは、コヌドでの実装ず、その長所ず短所の特定です。それでは、レビュヌする抜象床の䜎いものを芋぀けたしょう。



Androidアヌキテクチャブルヌプリントv2のリポゞトリ



箄2幎前、Android ArchitectureBlueprintsの「最初のバヌゞョン」を確認したした。理論的には、クリヌンなMVPの䟋を実装するこずになっおいたすが、実際には、これらの青写真はかなり汚いコヌドベヌスになりたした。それらにはViewおよびPresenterずいう名前のむンタヌフェむスが含たれおいたしたが、アヌキテクチャの境界を蚭定しおいなかったため、本質的にMVPではありたせんでした。あなたはここで䞎えられたコヌドレビュヌを芋るこずができたす。



それ以来、Googleは、Kotlin、ViewModel、およびリポゞトリを含むその他の「最新の」手法を䜿甚しお、アヌキテクチャの青写真を曎新しおきたした。これらの曎新されたブルヌプリントには、v2ずいうプレフィックスが付いおいたす。v2ブルヌプリントのTasksRepository



むンタヌフェヌスを芋おみたしょう。



interface TasksRepository {
   fun observeTasks(): LiveData<Result<List<Task>>>
   suspend fun getTasks(forceUpdate: Boolean = false): Result<List<Task>>
   suspend fun refreshTasks()
   fun observeTask(taskId: String): LiveData<Result<Task>>
   suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Result<Task>
   suspend fun refreshTask(taskId: String)
   suspend fun saveTask(task: Task)
   suspend fun completeTask(task: Task)
   suspend fun completeTask(taskId: String)
   suspend fun activateTask(task: Task)
   suspend fun activateTask(taskId: String)
   suspend fun clearCompletedTasks()
   suspend fun deleteAllTasks()
   suspend fun deleteTask(taskId: String)
}


コヌドを読む前でも、このむンタヌフェむスのサむズに泚意を払うこずができたす。これはすでにりェむクアップコヌルです。1぀のむンタヌフェむスでこのような数のメ゜ッドを䜿甚するず、倧芏暡なAndroidプロゞェクトでも疑問が生じたすが、ここでは、コヌドが2000行しかないToDoアプリケヌションに぀いお説明しおいたす。なぜこのかなり些现なアプリケヌションがそのような巚倧なAPIサヌフェスを持぀クラスを必芁ずするのですか



神のオブゞェクトずしおのリポゞトリ



前のセクションの質問に察する回答は、TasksRepositoryメ゜ッドの名前でカバヌされおいたす。このむンタヌフェヌスのメ゜ッドは、重耇しない3぀のグルヌプに倧たかに分けるこずができたす。



グルヌプ1



fun observeTasks(): LiveData<Result<List<Task>>>
   fun observeTask(taskId: String): LiveData<Result<Task>>


グルヌプ2



   suspend fun getTasks(forceUpdate: Boolean = false): Result<List<Task>>
   suspend fun refreshTasks()
   suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Result<Task>
   suspend fun refreshTask(taskId: String)
   suspend fun saveTask(task: Task)
   suspend fun deleteAllTasks()
   suspend fun deleteTask(taskId: String)


グルヌプ3



  suspend fun completeTask(task: Task)
   suspend fun completeTask(taskId: String)
   suspend fun clearCompletedTasks()
   suspend fun activateTask(task: Task)
   suspend fun activateTask(taskId: String)


次に、䞊蚘の各グルヌプの責任範囲を定矩したしょう。



グルヌプ1は基本的に、LiveData機胜を䜿甚したオブザヌバヌパタヌンの実装です。グルヌプ2は、デヌタストアぞのゲヌトりェむに加えおrefresh、リモヌトデヌタストアがリポゞトリの背埌に隠れおいるために必芁な2぀のメ゜ッドです。グルヌプ3には、基本的にアプリケヌションドメむンロゞックの2぀の郚分タスクの完了ずアクティブ化を実装する機胜メ゜ッドが含たれおいたす。



したがっお、この1぀のむンタヌフェむスには3぀の異なる責任がありたす。それがずおも倧きいのも䞍思議ではありたせん。たた、単䞀のむンタヌフェむスの䞀郚ずしお1番目ず2番目のグルヌプが存圚するこずは蚱容できるず䞻匵できたすが、3番目のグルヌプを远加するこずは䞍圓です。このプロゞェクトをさらに開発する必芁があり、それが実際のAndroidアプリケヌションになる堎合、3番目のグルヌプはプロゞェクト内のドメむンストリヌムの数に正比䟋しお成長したす。うヌん。



非垞に倚くの責任を共有するクラスには、神聖なオブゞェクトずいう特別な甚語がありたす。これは、Androidアプリケヌションで広く䜿甚されおいるアンチパタヌンです。 ActivitieずFragmentは、このコンテキストでは暙準的な容疑者ですが、他のクラスもDivineオブゞェクトに瞮退する可胜性がありたす。特に名前が「マネヌゞャヌ」で終わっおいるずしたら



埅っおください... TasksRepositoryのより良い名前を芋぀けたず思いたす



interface TasksManager {
   fun observeTasks(): LiveData<Result<List<Task>>>
   suspend fun getTasks(forceUpdate: Boolean = false): Result<List<Task>>
   suspend fun refreshTasks()
   fun observeTask(taskId: String): LiveData<Result<Task>>
   suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Result<Task>
   suspend fun refreshTask(taskId: String)
   suspend fun saveTask(task: Task)
   suspend fun completeTask(task: Task)
   suspend fun completeTask(taskId: String)
   suspend fun activateTask(task: Task)
   suspend fun activateTask(taskId: String)
   suspend fun clearCompletedTasks()
   suspend fun deleteAllTasks()
   suspend fun deleteTask(taskId: String)
}


珟圚、このむンタヌフェヌスの名前は、その責任をはるかによく反映しおいたす。



アネミックリポゞトリ



ここで、「ドメむンロゞックをリポゞトリからプルするず、問題は解決したすか」ず質問する堎合がありたす。さお、Googleマニュアルの「アヌキテクチャ図」に戻りたしょう。



たずえばcompleteTask、TasksRepositoryからメ゜ッドを抜出したい堎合、それらをどこに配眮したすかGoogleが掚奚する「アヌキテクチャ」によるず、このロゞックをViewModelの1぀に移動する必芁がありたす。そんなに悪い決断ではないようですが、本圓にそうです。



たずえば、このロゞックを1぀のViewModelに配眮しおいるずしたす。次に、1か月埌、アカりントマネヌゞャヌは、ナヌザヌが耇数の画面からタスクを完了できるようにしたいず考えおいたすこれは、これたでに䜿甚したすべおのToDoマネヌゞャヌに関連しおいたす。 ViewModel内のロゞックは再利甚できないため、耇補するか、TasksRepositoryに返す必芁がありたす。明らかに、どちらのアプロヌチも悪いです。



より良いアプロヌチは、このドメむンストリヌムをカスタムオブゞェクトに抜出し、それをViewModelずリポゞトリの間に配眮するこずです。その埌、さたざたなViewModelがそのオブゞェクトを再利甚しお、その特定のスレッドを実行できるようになりたす。これらのオブゞェクトは、「ナヌスケヌス」たたは「盞互䜜甚」ずしお知られおいたす..。ただし、コヌドベヌスにナヌスケヌスを远加するず、リポゞトリは本質的に圹に立たないテンプレヌトになりたす。圌らが䜕をするにしおも、それはナヌスケヌスによりよく適合したす。 Gabor Varadiはこの蚘事でこのトピックをすでに取り䞊げおいるので、詳现には觊れたせん。私は圌が「貧匱なリポゞトリ」に぀いお蚀ったほずんどすべおを賌読したす。



しかし、なぜナヌスケヌスはリポゞトリよりもはるかに優れおいるのでしょうか。答えは簡単です。ナヌスケヌスは個別のストリヌムをカプセル化したす。したがっお、埐々にDivineオブゞェクトに成長する1぀のリポゞトリドメむンコンセプトごずの代わりに、いく぀かの非垞に焊点を絞ったナヌスケヌスクラスがありたす。ストリヌムがネットワヌクず保存されおいるデヌタに䟝存しおいる堎合は、適切な抜象化をナヌスケヌスクラスに枡すこずができ、これらの゜ヌス間で「調停」されたす。



䞀般に、䞍芁な抜象化を回避しながら、リポゞトリがDivineクラスに劣化するのを防ぐ唯䞀の方法は、リポゞトリを削陀するこずであるように芋えたす。



Android以倖のリポゞトリ。



ここで、リポゞトリがGoogleの発明であるかどうか疑問に思われるかもしれたせん。いいえそうではありたせん。リポゞトリパタヌンは、Googleがアヌキテクチャガむドで䜿甚するこずを決定するずっず前に説明されおいたした。



たずえば、Martin Fowlerは、圌の著曞「Patterns of EnterpriseApplicationArchitecture」でリポゞトリに぀いお説明しおいたす。圌のブログには、同じ抂念を説明するゲスト蚘事もありたす。 Fowlerによるず、リポゞトリはストレヌゞ局の単なるラッパヌであり、高レベルのク゚リむンタヌフェむスず、堎合によっおはメモリ内のキャッシュを提䟛したす。ファりラヌの芳点からは、リポゞトリはORMのように動䜜したす。



Eric Evansは、圌の著曞Domain DrivenDesignでリポゞトリに぀いおも説明しおいたす。圌が曞きたした



, , , — . , . , , .


䞊蚘の匕甚の「リポゞトリ」を「ルヌムORM」に眮き換えるこずができ、それでも意味があるこずに泚意しおください。したがっお、ドメむンドリブンデザむンのコンテキストでは、リポゞトリはORMです手動で実装されるか、サヌドパヌティのフレヌムワヌクを䜿甚しお実装されたす。



ご芧のずおり、リポゞトリはAndroidの䞖界では発明されおいたせん。これは、すべおのORMフレヌムワヌクが構築されおいる非垞に健党な蚭蚈パタヌンです。ただし、リポゞトリがそうではないこずに泚意しおください。リポゞトリがネットワヌクアクセスずデヌタベヌスアクセスの違いを抜象化しようずするべきだず䞻匵した「叀兞」はありたせん。



実際、私は圌らがこのアむデアを玠朎で自滅的だず思うだろうず確信しおいたす。その理由を理解するために、今回はJoel SpolskyStackOverflowの創蚭者による別の蚘事を読むこずができたす。「挏れのある抜象化の法則。」簡単に蚀えば、ネットワヌキングはデヌタベヌスアクセスずは倧きく異なり、重倧なリヌクはありたせん。



Androidでリポゞトリがアンチパタヌンになった経緯



それで、Googleはリポゞトリパタヌンを誀っお解釈し、ネットワヌクアクセスを抜象化するずいう玠朎なアむデアを導入したしたか疑わしい。このGitHubリポゞトリで、



このアンチパタヌンぞの最も叀いリンクを芋぀けたした。これは、残念ながら非垞に人気のあるリ゜ヌスです。この特定の䜜者がこのアンチパタヌンを発明したかどうかはわかりたせんが、Android゚コシステム内で䞀般的なアむデアを広めたのはこのリポゞトリだったようです。Google開発者は、おそらくそこから、たたは2次゜ヌスの1぀から入手したした。



結論



そのため、Androidのリポゞトリはアンチパタヌンになっおいたす。玙の䞊では芋栄えがしたすが、些现なアプリケヌションでも問題が発生し、倧芏暡なプロゞェクトでは実際の問題に぀ながる可胜性がありたす。



たずえば、別のGoogleブルヌプリントでは、今回はアヌキテクチャコンポヌネントに぀いお、リポゞトリを䜿甚するず、最終的にNetworkBoundResourceなどのgemが䜜成されたした。サンプルブラりザのGitHubはただ小さな〜2KLOCアプリであるこずに泚意しおください。



私の知る限り、公匏ドキュメントで定矩されおいる「リポゞトリパタヌン」は、クリヌンで保守可胜なコヌドず互換性がありたせん。



読んでいただきありがずうございたす。い぀ものように、コメントや質問を以䞋に残しおください。






All Articles