هو حقن التبعية كوين أو محدد الخدمة؟

المقدمة


يستخدم تطوير Android لـ DI تقليديًا Dagger 2 ، وهو إطار عمل قوي للغاية لإنشاء التعليمات البرمجية. ولكن هناك مشكلة: من الصعب على المبتدئين استخدامها. مبادئ DI نفسها بسيطة ومباشرة ، لكن Dagger يعقدها. يمكنك أن تشكو من التراجع المطلق في معرفة القراءة والكتابة للمبرمجين ، لكن المشكلة لن تختفي من هذا.


مع ظهور Kotlin ، أصبح من الممكن كتابة أشياء مريحة من المستحيل تقريبًا استخدام Java. أحد هذه الأشياء كان Koin ، وهو ليس DI ، ولكنه محدد خدمة ، والذي يعامله الكثيرون على أنه مضاد للنمط ، ولهذا السبب لا يستخدمه الكثير من الناس من حيث المبدأ. ولكن دون جدوى ، لأنه يحتوي على واجهة برمجة تطبيقات موجزة للغاية تبسط كتابة التعليمات البرمجية والحفاظ عليها.


في هذه المقالة ، أريد أن أساعد المبتدئين في معرفة الفرق بين حقن التبعية ومحدد الخدمة ، ولكن ليس كوين نفسها.


حقن التبعية


بادئ ذي بدء ، ما هو حقن التبعية؟ بكلمات بسيطة ، هذا عندما يقبل كائن تبعيات من الخارج ، بدلاً من إنشائها أو استخلاصها بنفسها. سوف أعطي مثالا على ذلك. لنفترض أن لدينا واجهة Engine، وتنفيذها ، وكذلك فئة Carتعتمد على Engine. بدون DI ، قد يبدو هذا


interface Engine
class DefaultEngine: Engine

class Car {
  private val engine: Engine = DefaultEngine()
}

fun main() {
  val car = Car()
}

إذا أعدنا كتابة الفصل Carباستخدام منهج DI ، فقد يحدث ذلك:


class Car(private val engine: Engine)

fun main() {
  val car = Car(DefaultEngine())
}

الأمر بسيط - Carلا يعرف الفصل من أين يأتي التنفيذ Engine، في حين أن استبدال هذا التطبيق سهل للغاية ، فقط قم بتمريره إلى المنشئ.


محدد الخدمة


Service Locator. – , . Koin ServiceLocator, , API.


object ServiceLocator {
  fun <reified T> register(factory: () -> T) { ... }
  fun <reified T> resolve(): T { ... }
}

, . :


interface Engine
class DefaultEngine: Engine

class Car {
  private val engine: Engine = ServiceLocator.resolve()
}

fun main() {
  ServiceLocator.register<Engine> { DefaultEngine() }
  val car = Car()
}

DI, Car , , – Car, . :


interface ServiceLocator {
  fun <reified T> register(factory: () -> T)
  fun <reified T> resolve(): T
}

class DefaultServiceLocator: ServiceLocator { ... }

class Car(private val serviceLocator: ServiceLocator) {
  private val engine = serviceLocator.resolve<Engine>()
}

fun main() {
  val serviceLocator = DefaultServiceLocator()
  serviceLocator.register<Engine> { DefaultEngine() }
  val car = Car(serviceLocator)
}

, Car , . .. . :


class Car(private val engine: Engine)

fun main() {
  ServiceLocator.register<Engine> { DefaultEngine() }
  val car = Car(ServiceLocator.resolve<Engine>())
}

هذا هو حقن التبعية النقي. مع كوين ، ستبدو كما يلي:


interface Engine
class DefaultEngine: Engine
class Car(private val engine: Engine)

fun carModule() = module {
  factory<Engine> { DefaultEngine() }
  factory { Car(get<Engine>()) }
}

fun main() {
  val koin = startKoin {
    modules(carModule())
  }.koin

  val car = koin.get<Car>()
}

للأسف ، ما زلنا بحاجة إلى الاتصال بـ Koin للحصول على التبعيات ، ولكن هذا لا يتعارض بأي حال من الأحوال مع مبادئ حقن التبعية.


تحديث حسب الطلبكرانيد سأعطي أبسط مثال على Dagger 2.


interface Engine
class DefaultEngine: Engine
class Car @Inject constructor(private val engine: Engine)

@Module
class AppModule {
  @Provides
  fun provideEngine(): Engine = DefaultEngine()
}

@Component(modules = [AppModule.class])
interface AppComponent {
  fun car(): Car
}

fun main() {
  // DaggerAppComponent –  ,   Dagger
  val daggerComponent = DaggerAppComponent.create()
  val car = daggerComponent.car()
}

All Articles