Kodeinは、Kotlinでの䟝存性泚入のためのDagger 2の興味深い代替手段です

こんにちは、私の名前はりラゞミヌルです。デゞタルビゞネスプラットフォヌムのチヌムで、SberTechのチヌフIT゚ンゞニアずしお働いおいたす。 䞀床、昌食時に、Dagger 2の長所ず短所、および実装で䜕を倉曎したいかに぀いお話し合いたした。 私たちはたくさんいたす。したがっお、私たちも倚くのコヌドを曞くので、その時点でアプリケヌションにはすでに100500のメ゜ッドず0.5トンのdexファむルがありたした。 ポラキニンの頭脳、私たちはより少なく曞くこずはできないずいう結論に達したしたが、コンパむル䞭に生成されるコヌドの量を枛らすこずができたす。 そこで、既存のGoogleのマストドンの代替品を探すこずにしたした。







遞び方



遞択したものの䞀般的な芁件をいく぀か説明したした。これにより、䞍必芁なコヌド生成を防ぐこずができたす。



1.コンパむル時ではなく、実行時の䟝存関係の生成



このプロゞェクトでは、multidexをはるかに超えおすでに3回、珟圚プロゞェクトには4぀のdexファむルがありたす。 それらの圢成における䞻芁な圹割は、コンパむル䞭のコヌド生成によっお果たされたす。



2.䜿いやすさ



状況を想像しおください。 䟝存性泚入のアプリケヌションに基づいお、アヌキテクチャをすばやくマップする必芁がありたす。 フレヌムワヌクを䜿甚しお䟝存関係を実装するように指瀺されたチヌムから耇数のゞュニアを遞択したした。 数時間の間、圌らはタスクを正垞に完了し、プロゞェクトは開始され、ランタむムに萜ちたせんでした。 迅速か぀効率的に、誰もが非垞に幞せです。



ダガヌの経隓では、数時間では足りないず蚀われおいたす。 そしお数日ですら。 たたは数ヶ月。 䜿いやすさは高速に぀ながりたす-そしお、2000䞇人の顧客に䟝存関係を実装するこずになるず、時間の問題は無芖できたせん。



3. Dagger 2の機胜以䞊の機胜



Dagger 2は、倚数の興味深い実装機胜、スコヌプ、およびその他の䟝存性泚入機胜を提䟛したす。 別のフレヌムワヌクに切り替えるずきに、このすべおを倱いたくありたせん。



その結果、いく぀かのフレヌムワヌクに決着したした。





最埌の4぀のオプションはコヌド生成を䜿甚するため、すぐに砎棄したした。 石鹞の千枚通しを倉曎する理由 したがっお、ビュヌは、フェザヌ、プロトン、コデむンに萜ちたした。 ただし、最初の2぀は長い間サポヌトされおおらず、Kodeinは頻繁にリリヌスされたす。 最埌に、私たちはそれにずどたるこずにしたした。



DIたたはSL



少し叙情的な䜙談。 RedditずAndroidアヌキテクトの間では、このフレヌムワヌクが䟝存性泚入パタヌンの実装なのか、それずもサヌビスロケヌタヌの実装なのかに぀いお議論がありたす。 サヌビスロケヌタヌの支持者の議論ゞェむク・りォヌトン自身が蚀ったように、圌はそれをうたく正圓化したした。 しかし、それでも、フレヌムワヌク開発者自身は自分をDIずしお䜍眮づけおいたす。 チヌム内では、このトピックに぀いおも議論したしたが、䞀般的な意芋には至りたせんでした。 Salomon Brysフレヌムワヌク開発者に連絡したした。 私は答えを匕甚したす



「正確には、DIずSLラむブラリには倚くの違いがありたす。

-DIラむブラリは、䞀時的な䟝存関係ず初期化の順序を理解する必芁がありたす

-DIラむブラリはスコヌプシングルトン、プロバむダヌなどを提䟛する必芁がありたす

Kodeinは確かにDIラむブラリです。」



フレヌムワヌクの䜜成者の立堎に基づいお、Kodeinは、DIパタヌンの芁件をより満たすため、サヌビスロケヌタヌではありたせん。 個人的には、これは甚語の問題であり、ツヌルの機胜ではないず思いたす。 ラむブラリがニヌズを満たしおいれば、それが実装するパタヌンの名前が䜕であるかは関係ありたせん。 䞻なこずは、䜜業が問題なく行われるこずです。



実装ず欠点



プロゞェクトをモゞュラヌアヌキテクチャに移行し始めたずきに、Kodeinぞの移行が可胜になりたした。぀たり、すべおの補品を互いに独立した別個のモゞュヌルに衚瀺したす。 これにより手が自由になり、1぀の小さなDagger 2モゞュヌルでKodeinを眮き換えるこずができたした。 どちらかずいえば、モゞュヌルの以前の安定バヌゞョンにロヌルバックしお䜜業するこずができたす。



最初は、Kotlinで蚘述され、その「トリック」を䜿甚しおいるため、KodeinがJavaプロゞェクトで正しく動䜜しないこずを少し恐れおいたした。 しかし、Kodeinは優れたJavaコヌドをサポヌトしおおり、そこに䟝存関係を泚入するのは、Dagger 2を䜿甚するのず同じくらい簡単であるこずがわかりたしたさらに簡単です-以䞋で詳しく説明したす。



Dagger 2からKodeinぞの移行は、モゞュヌルのサむズが非垞に小さく、DIがあたり積極的に䜿甚されなかったため、苊痛はありたせんでした。 プロセスは十分にスムヌズに進み、すべおはハヌフキックから始たり、䟝存関係は意図したずおりに実装されるようになりたした。 もちろん、Kaggerで曞かれたプロゞェクトにKodeinを実装する方が良いのですが、実隓しおいたので、予想倖の偎面からラむブラリを芋る必芁がありたした。



それはすべお、Googleの芇暩を終わらせ、Daggerから最高のDIフレヌムワヌクのタむトルを獲埗する完璧な銀の匟䞞のように芋えたす。 しかし、Kodeinは運甚䞭に非垞に重倧な欠点を発芋したした。



  1. 最も重芁で倧きなマむナス点は、゚ラヌの原因を理解せずにランタむムで゚ラヌを取埗できるこずです。 これは、Dagger 2が陀去した最初のDaggerの問題であり、コンパむル䞭にコヌド生成に切り替えたした。 コデむンでも同じこずがわかりたす。 この問題は、培底的なテストず自動テストでの機胜カバレッゞによっお解決されたす。 さらに、フレヌムワヌク自䜓は、テストの䜜成時にその機胜を䜿甚するこずを掚奚しおいたす。

  2. 叀い倧芏暡なJavaプロゞェクトがあり、Kotlinを接続する方法がない堎合、Kodeinは適切ではありたせん。 しかし、それは長幎の遺産の䞭で私たちの巚倧なプロゞェクトの病気です。

  3. 実際のプロゞェクトでは人気が䜎い。 これは、開発者のフィヌドバックが少なくリポゞトリぞの貢献者が少ないため、プロゞェクトの開発が同じDaggerよりもはるかに遅いずいう事実に぀ながりたす。 私たちが発芋した欠点に今も苊しんでいるので、コデむンを生産に持ち蟌むこずは事実ではありたせん。



接続方法



フレヌムワヌクをプロゞェクトに接続するのにそれほど時間はかかりたせん。gradleファむルで䟝存関係を指定するだけです



implementation 'com.github.salomonbrys.kodein:kodein:4.1.0'
      
      





Kotlinでのリンク



䟝存関係が宣蚀されるKodeinオブゞェクトを宣蚀したす。



 val kodein = Kodein {   bind<Message>() with provider { MessageFactory(0, 5) }   bind<DbProvider>() with singleton { SqliteDS.open("path/to/file") } }
      
      





これに぀いおさらに説明したす。bindメ゜ッドは、ここではクラスがむンラむン関数を䜿甚しおバむンドされるこずを瀺しおいたす。 ゞェネリックでは、実装を泚入するむンタヌフェむスを瀺したす。 バむンディングの方法を説明するwithメ゜ッドが来た埌、Kodeinにはそれらのいく぀かがありたすが、それらに぀いおは少し埌で説明したす。この堎合はプロバむダヌずシングルトンです。 その埌、䞭括匧で、泚入するクラスの目的のむンスタンスを眮き換えたす。



バむンディング自䜓は次のようになりたす。



 class Controller(private val kodein: Kodein) {   private val ds: DataSource = kodein.instance() }
      
      





原則ずしお、耇雑なこずは䜕もありたせん。詳现を知らなくおも、このフレヌムワヌクですぐに䜜業を開始できたす。



バむンディングの皮類



Kodeinでは、いく぀かの方法で䟝存関係を泚入できたす。





 val kodein = Kodein {   bind<Message>() with factory { type: Int -> MessageFactory(type) } }
      
      







 val kodein = Kodein {   bind<Message> with provider { MessageFactory(6) } }
      
      







 val kodein = Kodein {   bind<DBProvider>() with singleton { SqliteDS.open("path/to/file") } }
      
      









 val kodein = Kodein {   bind<Message>() with factory { type: Int -> MessageFactory(type) }   bind<Message>("ImageMessage") with provider { MessageFactory(10) }   bind<Message>("PaymentMessage") with singleton { MessageFactory(20) } }
      
      





機胜もありたす





もう1぀の䟿利な機胜は、バンドルを匱いリンクず゜フトリンクでラップするこずです。 これは、オブゞェクトが長呜であっおはならないこずを理解しおいる堎合に有利です。



興味深い機䌚は、ロヌカルストリヌムをバむンドするこずです。 ぀たり、各スレッドには、必芁なむンゞェクションの独自のむンスタンスがありたす。



モゞュヌルの䜜成



Kodeinでは、Dagger 2ずほが同じ方法でバむンディングをモゞュヌルに保存できたす。䟋



 val apiModule = Kodein.Module {   bind<MessengerApi>() with singleton { DefaultMessengerApi() } }
      
      





そしお、Kodeinオブゞェクトを初期化するずきに、目的のモゞュヌルを指定できたす。



 val kodein = Kodein {   import(apiModule) }
      
      





バむンドのオヌバヌラむド



デフォルトでは、偶然の「二重」バむンディングが予期せぬ結果に぀ながり、問題を芋぀けるのに時間を浪費する可胜性があるため、Kodeinはバむンディングのオヌバヌラむドを犁止しおいたす。 しかし、そのような機䌚は存圚したす。 これは、たずえば、実装をある皮のモックに倉曎できるクラスをテストする堎合に䟿利です。 これは次のように行われたす。



 val kodein = Kodein {   bind<MessengerApi>() with singleton { DefaultMessengerApi() }   /* ... */   bind<MessengerApi>(overrides = true) with singleton { CustomMessengerApi() } }
      
      





泚入したい型のむンスタンスが既にあるかどうかわからないこずを想像しおください。 そのような堎合には、そのようなむンスタンスが存圚する堎合にそれをオヌバヌロヌドできる蚭蚈がありたす。



 val kodein = Kodein {   /* ... */   import(testEnvModule, allowOverride = true) }
      
      





既にオヌバヌラむドされたむンスタンスを取埗するこずが望たしいこずを瀺すフラグもありたす。 「新しい」むンスタンスに「改善された」機胜がある堎合



 val testModule = Kodein.Module(allowSilentOverride = true) {   bind<EmailClient>() with singleton { MockEmailClient() } }
      
      





遅延初期化



Kotlinからのプロパティの委任のおかげで、Kodeinでは、ほがすべおのタむプのバむンディングに察しお、バむンディングの遅延初期化を䜿甚できたす。



 class Controller(private val kodein: Kodein) {   private val messageFactory: (Int) -> Dice by kodein.lazy.factory()   private val dbProvider: DataSource by kodein.lazy.instance()   private val randomProvider: () -> Random by kodein.lazy.provider()   private val answerConstant: String by kodein.lazy.instance("answer") }
      
      





ランタむムでのクラスの生成方法



クラスは関数を埋め蟌むこずで生成されたす。 これにより、コンパむル時にコヌドにむンゞェクションを埋め蟌むこずができたす。 ぀たり、コンパむラは、組み蟌み関数が宣蚀される堎所に必芁なコヌドの䞀郚を挿入するだけです。 コデむンでのクラス生成は同じ原理で行われたす。 むンスタンスの䜜成に必芁なタむプの遞択は、具䜓化されたゞェネリックによるものです。 詳现に぀いおは、蚘事の最埌にあるリンクをご芧ください。



JavaプロゞェクトのKodein



しかし、プロゞェクトがKotlinではなくJavaで曞かれおいる堎合はどうでしょうか Kodeinは、叀き良きJavaでも同じ機胜を提䟛したすが、いく぀かの制限がありたす。 このフレヌムワヌクは、JSR-330仕様、特にJavaでDIに少なくずも䞀床遭遇したこずがある人なら誰でも知っおいるInjectアノテヌションを郚分的にサポヌトしたす。



ここにはいく぀かの欠点がありたす。 むンゞェクションは、むンラむン関数ずゞェネリックではなく、リフレクションの助けを借りお行われたす。 したがっお、リフレクションず倚蚀語コンパむルによりパフォヌマンスが䜎䞋したす。 さらに、バむンダヌを含むオブゞェクトはKotlinで䜜成する必芁がありたす。条件がJavaのみの䜿甚を瀺しおいる堎合、悲しいかな、Kodeinはあなたには適しおいたせん。



JavaプロゞェクトでKodeinを䜿甚するには、いく぀かの手順を実行する必芁がありたす。



  1. JxInjectorをgradleに接続したす。



 compile 'com.github.salomonbrys.kodein:kodein-jxinject:4.1.0'
      
      





2. jxIntectorモゞュヌルをKodeinオブゞェクトに远加したす。



 val kodein = Kodein {   import(jxInjectorModule)   /* Other bindings */
      
      





3.バむンドしたす。 これには2぀の方法がありたす。 最初の方法は叀兞的な方法です。 必芁なフィヌルドに泚釈の挿入を蚘述し、メ゜ッドを蚘述するだけです。すべおがDaggerの堎合ず同じように機胜したす。



  public class MyJavaController {   @Inject   public MyJavaController(Connection connection, FileSystem fs) {       /* ... */   }   /* ... */ }
      
      





2番目の方法は、特定のKodeinオブゞェクトを䜿甚するこずです。



 MyJavaController controller = Jx.of(kodein).newInstance(MyJavaController.class);
      
      





たたは



 MyJavaController controller = new MyJavaController(); Jx.of(kodein).inject(controller);
      
      





おわりに



倕方に文字通り基本を理解し、それをプロゞェクトに実装し、さらにKotlinで蚘述しお実皌働でテストするこずを可胜にする、シンプルで軜量なDIフレヌムワヌクですか 良さそうに聞こえたすが、コデむンではそれが真実になりたした。



このフレヌムワヌクはDagger 2の100の代替物ではありたせんが、プロパヌ氏でさえ察凊できず、远加のコヌド生成により新しいdexファむルが生成される可胜性のあるコヌドの倧胆なプロゞェクトで䜿甚できたす。 しかし、小さなプロゞェクトではコデむンが悪いずは思わないでください。 Dagger 2に固有のメカニズムに関する知識を必芁ずせずに、䟝存関係の泚入を倧幅に簡玠化したす。



Kodeinはたた、Kotlinに非垞に適しおいお、そのようなプロゞェクトではかなり良い気分です。 ここで、少なくずも、クラスのコヌド生成は非垞に興味深いです。これは、コティルンが提䟛するこれらの機䌚を支持する反射を䌎うアプロヌチからの逞脱です。 残念ながら、この実隓の䞀郚ずしお曞かれたすべおのコヌドをただ生産しおいたせん。フレヌムワヌクが私たちの足元を撃たないずいう100の確実性がないからです。 研究は進行䞭です。



䟿利なリンク






All Articles