المقدمة
يستخدم تطوير 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() {
val daggerComponent = DaggerAppComponent.create()
val car = daggerComponent.car()
}