لم أجد أدلة مفهومة لأولئك الذين
Kodein
لأول مرة ، والوثائق ليست شفافة ومتسقة في جميع الأماكن ، لذلك أريد أن أشارككم السمات الرئيسية للمكتبة. سيتم إصدار بعض ميزات المكتبة ، ولكن هذا هو الجزء المتقدم بشكل أساسي. ستجد هنا كل شيء للبدء بشكل طبيعي والبدء في تطبيق التبعيات مع
Kodein
أثناء قراءة المقال. تستند المقالة إلى
Kodein 5.3.0
، نظرًا لأن
Kodein 6.0.0
يتطلب
Support Library 28
أو
AndroidX
وسيكون بعيدًا عن أن يتحول الجميع إليها لأن العديد من مكتبات الأطراف الثالثة لم تقدم بعد إصدارات متوافقة.

Kodein
هي مكتبة لتنفيذ حقن التبعية (DI). إذا لم تكن على دراية بهذا المفهوم ، فاقرأ بداية
المقال حول Dagger2 ، حيث يشرح المؤلف بإيجاز الجوانب النظرية لـ DI.
في هذه المقالة ، سننظر في كل شيء باستخدام مثال Android ، ولكن وفقًا للمطورين ، يتصرف Kodein كما هو في جميع الأنظمة الأساسية التي تدعمها Kotlin (JVM ، Android ، JS ، Native).
التثبيت
نظرًا لحقيقة أن Java تحتوي على
type erasure
، تنشأ مشكلة - يقوم المحول البرمجي بمسح النوع العام. على مستوى الرمز البريدي ،
List<String>
وقائمة
List<Date>
ليست سوى
List
. ومع ذلك ، لا تزال هناك طريقة للحصول على معلومات حول الأنواع العامة ، ولكنها ستكلف الكثير وتعمل فقط على JVM و Android. في هذا الصدد ، يقترح مطورو
Kodein
استخدام واحدة من اثنين من التبعيات: واحد يتلقى معلومات حول الأنواع المعممة (
kodein-generic
) أثناء العمل ، والآخر لا (
kodein-erased
). على سبيل المثال ، عند استخدام
List<String>
kodein-erased
List<String>
List<Date
> سيتم حفظها على شكل
List<*>
، وعند استخدام
kodein-generic
سيتم حفظ كل شيء جنبًا إلى جنب مع النوع المحدد ، أي ،
List<String>
List<Date>
على التوالي.
كيف تختار؟
لا تكتب تحت JVM - استخدم
kodein-erased
، وإلا فإنه من المستحيل.
اكتب تحت JVM
kodein-erased
الأداء مهمة للغاية بالنسبة لك - يمكنك استخدام
kodein-erased
، ولكن كن حذرًا ، فقد تكون هذه التجربة غير متوقعة بالمعنى السيئ لهذه الكلمات. إذا كنت تقوم بإنشاء تطبيق منتظم دون أي متطلبات أداء خاصة ، فاستخدم
kodein-generic
.
في النهاية ، إذا فكرت في تأثير DI على الأداء ، فغالبًا ما يتم إنشاء الجزء الأكبر من التبعيات مرة واحدة ، أو يتم إنشاء التبعيات لإعادة الاستخدام المتكرر ، فمن غير المرجح أنه مع مثل هذه الإجراءات ، يمكنك التأثير بشكل كبير على أداء التطبيق الخاص بك.
لذلك ، تثبيت:
أولا - في build.gradle بين المستودعات يجب أن يكون jcenter () ، إذا لم يكن هناك - إضافة.
buildscript { repositories { jcenter() } }
بعد ذلك ، في كتلة التبعيات ، أضف واحدة من التبعيات الأساسية المذكورة أعلاه:
implementation "org.kodein.di:kodein-di-generic-jvm:$version"
implementation "org.kodein.di:kodein-di-erased-jvm:$version"
بما أننا نتحدث عن Android ، فستكون هناك مزيد من التبعيات. يمكنك بالطبع الاستغناء عنها ، ستعمل Kodein بشكل طبيعي ، لكن لماذا ترفض الميزات الإضافية المفيدة لنظام Android (سأتحدث عنها في نهاية المقالة)؟ الخيار لك ، لكنني أقترح إضافته.
هناك أيضا خيارات هنا.
أولاً ، أنت لا تستخدم
SupportLibrary
implementation "org.kodein.di:kodein-di-framework-android-core:$version"
الثاني - الاستخدام
implementation "org.kodein.di:kodein-di-framework-android-support:$version"
ثالثا - أنت تستخدم AndroidX
implementation "org.kodein.di:kodein-di-framework-android-x:$version"
نبدأ في خلق التبعيات
باستخدام
Dagger2
، اعتدت على إنشاء وتهيئة التبعيات عند بدء تشغيل التطبيق ، في فئة التطبيق.
مع Kodein ، يتم ذلك مثل هذا:
class MyApp : Application() { val kodein = Kodein { } }
إعلانات التبعية تبدأ دائمًا بـ
bind<TYPE>() with
العلامات
تعد علامات تبعية Kodein ميزة مشابهة في وظيفة "
Qualifier
من
Dagger2
. في
Dagger2
يلزمك إما إجراء
Qualifier
منفصلة أو استخدام
@Named("someTag")
، وهو في الواقع أيضًا
Qualifier
. خلاصة القول بسيطة - وبهذه الطريقة يمكنك التمييز بين اثنين من التبعية من نفس النوع. على سبيل المثال ، تحتاج إلى الحصول على
ontext
تطبيق أو
Activity
معين حسب الموقف ، لذلك تحتاج إلى تحديد علامات لهذا عند الإعلان عن التبعيات. يسمح
Kodein
بالإعلان عن تبعية واحدة دون علامة ، وستكون القاعدة واحدة وإذا لم تحدد العلامة عند تلقي التبعية ، فسوف نحصل عليها ، وسيلزم وضع علامة على الآخرين ، وعندما يتم تلقي التبعية ، يجب تحديد العلامة.
val kodein = Kodein { bind<Context>() with ... bind<Context>(tag = "main_activity") with ... bind<Context>(tag = "sale_activity") with ... }
المعلمة
tag
من النوع
Any
، لذلك يمكنك استخدام أكثر من مجرد سلاسل. ولكن تذكر أن الفئات المستخدمة
hashCode
يجب أن تطبق أساليب
equals
و
hashCode
. من الضروري دائمًا تمرير علامة إلى دالة كوسيطة مسماة ، بغض النظر عما إذا كنت تنشئ تبعية أم تتلقىها.
أنواع حقن التبعية
هناك عدة طرق لتوفير التبعيات في
Kodein
،
Kodein
من الأساسيات - إنشاء
Kodein
مفردة. سوف يعيش المفرد في إطار مثيل
Kodein
تم إنشاؤه.
تقديم المفردة
لنبدأ بمثال:
val kodein = Kodein { bind<IMyDatabase>() with singleton { RoomDb() } }
وبالتالي ، فإننا نقدم (نوفر)
IMyDatabase
، والتي سيتم من خلالها إخفاء مثيل
RoomDb
. سيتم إنشاء مثيل لـ
RoomDb
بناءً على الطلب الأول من التبعية ، ولن يتم
Kodein
حتى يتم
Kodein
مثيل
Kodein
جديد. يتم إنشاء مفردة متزامنة ، ولكن إذا رغبت في ذلك ، يمكن جعلها غير متزامنة. سيؤدي ذلك إلى زيادة الإنتاجية ، ولكن يجب أن تفهم المخاطر التي تتبع.
val kodein = Kodein { bind<IMyDatabase>() with singleton(sync = false) { RoomDb() } }
إذا كنت بحاجة إلى إنشاء مثيل تبعية ليس في المكالمة الأولى ، ولكن بعد إنشاء مثيل
Kodein
، استخدم وظيفة أخرى:
val kodein = Kodein { bind<IMyDatabase>() with eagerSingleton { RoomDb() } }
خلق باستمرار مثيل جديد من التبعية
من الممكن إنشاء أحرف مفردة ، ولكن باستمرار عند الوصول إلى تبعية للحصول على مثيل جديد منه. لهذا الغرض ، يتم استخدام وظيفة
provider
:
val kodein = Kodein { bind<IMainPresenter>() with provider { QuantityPresenter() } }
في هذه الحالة ، في كل مرة نطلب فيها تبعية
IMainPresenter
، سيتم إنشاء مثيل جديد لـ
QuantityPresenter
.
قم بإنشاء مثيل جديد من التبعية باستمرار وقم بتمرير المعلمات إلى المُنشئ
يمكنك الحصول على مثيل جديد منه في كل مرة تقوم فيها بإضافة تبعية ، كما في المثال السابق ، ولكن يمكنك تحديد المعلمات لإنشاء التبعية. يمكن أن تكون المعلمات بحد أقصى
5 . لهذا السلوك ، استخدم طريقة
factory
.
val kodein = Kodein { bind<IColorPicker>() with factory { r: Int, g: Int, b: Int, a: Int -> RgbColorPicker(r, g, b, a) } }
في كل مرة نقوم بإنشاء مثيل مخزنة مؤقتًا اعتمادًا على المعلمات
عند قراءة الفقرة السابقة ، قد تعتقد أنه سيكون من الجيد أن تتلقى ليس في كل مرة مثيل جديد وفقًا للمعايير التي تم تمريرها ، ولكن للحصول على نفس مثيل التبعية على نفس المعلمة.
val kodein = Kodein { bind<IRandomIntGenerator>() with multiton { from: Int, to: Int -> IntRandom(from, to) } }
في المثال أعلاه ، عندما نحصل على التبعية لأول مرة مع المعلمتين
5
و
10
IntRandom(5, 10)
نسخة جديدة من
IntRandom(5, 10)
، عندما نسمي التبعية مرة أخرى بنفس المعلمات ، سنحصل على المثيل الذي تم إنشاؤه مسبقًا. وبالتالي ، يتم الحصول على
map
من المفرد مع التهيئة البطيئة. الحجج ، كما في حالة
factory
الحد الأقصى
5 .
كما هو الحال مع المفردات ، يمكنك تعطيل التزامن هنا.
val kodein = Kodein { bind<IRandomIntGenerator>() with multiton(sync = false) { from: Int, to: Int -> IntRandom(from, to) } }
استخدام الروابط الضعيفة والضعيفة في كودين
عند توفير التبعيات باستخدام
singleton
أو
multiton
يمكنك تحديد نوع المرجع للمثيل المخزن. في الحالة المعتادة ، التي نظرنا فيها أعلاه - سيكون هذا الرابط
strong
المعتاد. لكن من الممكن استخدام روابط
weak
. إذا كنت جديدًا على هذه المفاهيم ،
فاحصل هنا .
وبالتالي ، قد يتم إعادة إنشاء نغماتك الفردية كجزء من دورة حياة التطبيق ، أو قد لا تكون كذلك.
val kodein = Kodein { bind<IMyMap>() with singleton(ref = softReference) { WorldMap() } bind<IClient>() with singleton(ref = weakReference) { id -> clientFromDB(id) } }
مفردة منفصلة لكل تيار
هذا هو نفس المفرد ، ولكن لكل مؤشر ترابط يطلب تبعية ، سيتم إنشاء مفردة. للقيام بذلك ، استخدم المعلمة المألوفة
ref
.
val kodein = Kodein { bind<Cache>() with singleton(ref = threadLocal) { LRUCache(16 * 1024) } }
الثوابت كما التبعيات القابلة للتضمين
يمكنك توفير الثوابت كما التبعيات. تسترعي الوثائق الانتباه إلى حقيقة أنه مع
Kodein
يجب عليك
Kodein
ثوابت من أنواع بسيطة دون وراثة أو واجهات ، على سبيل المثال البدائية أو فئات البيانات.
val kodein = Kodein { constant(tag = "maxThread") with 8 constant(tag = "serverURL") with "https://my.server.url"
إنشاء التبعيات دون تغيير النوع
على سبيل المثال ، تريد توفير التبعية كحرف مفرد ، ولكن لا تخفيها خلف الواجهة. لا يمكنك ببساطة تحديد النوع عند الاتصال واستخدام
from
بدلاً من
with
.
val kodein = Kodein { bind() from singleton { Gson() }
سيكون التبعية في المثال أعلاه نوع الإرجاع للدالة ، أي أنه سيتم
Gson
تبعية لنوع
Gson
.
إنشاء تبعيات فئة فرعية من الطبقة العليا أو واجهة
يسمح
Kodein
بتوفير التبعية بطرق مختلفة لأحفاد فئة أو فئات معينة تقوم بتنفيذ واجهة واحدة.
val kodein = Kodein { bind<Animal>().subTypes() with { animalType -> when (animalType.jvmType) { Dog::class.java -> eagerSingleton { Dog() } else -> provider { WildAnimal(animalType) } } }
يمكن أن تكون فئة
Animal
إما فئة
.subtypes
أو واجهة ، باستخدام
.subtypes
نحصل على نوع
animalType
أنواع
animalType
النوع
TypeToken<*>
، والذي يمكننا من خلاله بالفعل الحصول على فئة Java ،
TypeToken<*>
لذلك ، نقدم تبعية بطرق مختلفة. يمكن أن تكون هذه الميزة مفيدة إذا كنت تستخدم
TypeToken
أو مشتقاته كمعلمة مُنشئ لعدد من الحالات. وبهذه الطريقة أيضًا ، يمكنك تجنب التعليمات البرمجية غير الضرورية بنفس إنشاء التبعية لأنواع مختلفة.
قم بإنشاء تبعيات تحتاج إلى تبعيات أخرى كمعلمات
في أغلب الأحيان ، لا نقوم فقط بإنشاء فصل بدون معلمات تبعية ، ولكننا ننشئ فصلًا نحتاج إلى تمرير المعلمات إليه.
class ProductGateway(private val api: IProductApi, private val dispatchers: IDispatchersContainer) : IProductGateway
لإنشاء فئة ذات تبعيات تم إنشاؤها مسبقًا في
Kodein
يكفي تمرير استدعاء دالة () كمعلمات. في هذه الحالة ، ترتيب الخلق غير مهم.
bind<IDispatchersContainer>() with singleton { DispatchersContainer() } bind<IProductGateway>() with singleton { ProductGateway(instance(), instance()) } bind<IProductApi>() with singleton { ProductApi() }
بدلاً من
instance()
قد تكون هناك مكالمات إلى
provider()
أو
factory()
؛ سنلقي نظرة فاحصة على هذه الطرق في القسم الخاص بالحصول على التبعيات وتنفيذها.
قم بإنشاء تبعية عن طريق استدعاء أسلوب التبعية الذي تم إنشاؤه مسبقًا
لا يبدو هذا جيدًا جدًا ، ولكن يمكنك استدعاء
instance<TYPE>
للحصول على فصل دراسي نوفره بالفعل في مكان ما والاتصال بأسلوب هذه الفئة للحصول على تبعية جديدة.
bind<DataSource>() with singleton { MySQLDataSource() } bind<Connection>() with provider { instance<DataSource>().openConnection() }
وحدات
باستخدام
Dagger2
، اعتدت
Dagger2
التبعيات
Dagger2
. في
Kodein
، للوهلة الأولى ، كل شيء لا يبدو جيدا جدا. تحتاج إلى إنشاء الكثير من التبعيات مباشرةً في فئة
Application
، وأنا شخصياً لا أحبها حقًا. ولكن هناك حل ، يسمح لك
Kodein
أيضًا بإنشاء وحدات ، ثم توصيلها في تلك الأماكن عند الضرورة.
val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } bind<HttpClient>() with singleton { provideHttpClient() } } val kodein: Kodein = Kodein { import(appModule) bind<ISchedulersContainer>() with singleton { SchedulersContainer() }
ولكن كن حذرًا ، الوحدات النمطية عبارة عن حاويات تُعلن عن طرق للحصول على التبعيات ؛ فهي في حد ذاتها لا تنشئ فئات. لذلك ، إذا أعلنت استلام التبعية كحرف مفرد في الوحدة النمطية ، ثم قمت باستيراد هذه الوحدة النمطية إلى حالتين مختلفتين من
Kodein
،
Kodein
على فردين مختلفين ، واحد لكل مثيل
Kodein
.
أيضا ، يجب أن يكون اسم كل وحدة فريدة من نوعها. ومع ذلك ، إذا كنت بحاجة إلى استيراد وحدة نمطية من مشروع آخر ، فمن الصعب ضمان تفرد الاسم ؛ لذلك ، يمكنك إعادة تسمية الوحدة النمطية أو إضافة بادئة لاسمها.
import(apiModule.copy(name = "firstAPI")) import(secondApiModule.copy(prefix = "secondAPI-"))
اعتدت على العمل عندما تكون الوحدات معتمدة على بعضها البعض وتشكل نوعا من التسلسل الهرمي.
Kodein
استيراد كل وحدة نمطية إلى
Kodein
مرة واحدة ، لذلك ، إذا حاولت استيراد وحدتين لهما نفس الوحدات التابعة في
Kodein
، فسوف يتعطل التطبيق. الحل بسيط - تحتاج إلى استخدام
importOnce(someModule)
للاستيراد ، والذي سيتحقق مما إذا كانت الوحدة النمطية التي تحمل الاسم نفسه قد تم استيرادها مسبقًا ، ثم الاستيراد إذا لزم الأمر.
على سبيل المثال ، في مثل هذه الحالات ، سيتعطل التطبيق:
val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val secondModule = Kodein.Module("second") { import(appModule) } val thirdModule = Kodein.Module("third") { import(appModule) } val kodein: Kodein = Kodein { import(secondModule) import(thirdModule) }
val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val secondModule = Kodein.Module("second") { importOnce(appModule) } val thirdModule = Kodein.Module("third") { import(appModule) } val kodein: Kodein = Kodein { import(secondModule) import(thirdModule) }
ولكن إذا كانت استدعاء
importOnce
في محاولة اتصال ثانية ،
importOnce
كل شيء. كن حذرا.
val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val secondModule = Kodein.Module("second") { import(appModule) } val thirdModule = Kodein.Module("third") { importOnce(appModule) } val kodein: Kodein = Kodein { import(secondModule) import(thirdModule) }
الميراث
إذا كنت تستخدم نفس الوحدة مرتين ، فسيتم إنشاء تبعيات مختلفة ، ولكن ماذا عن الميراث وتطبيق السلوك المماثل
Dagger2
في
Dagger2
؟ كل شيء بسيط ، تحتاج فقط إلى أن ترث من مثيل
Kodein
وسوف تحصل على الوصول إلى جميع تبعيات الوالد في الوريث.
val kodein: Kodein = Kodein { bind<ISchedulersContainer>() with singleton { SchedulersContainer() }
إعادة تعريف
بشكل افتراضي ، لا يمكنك تجاوز التبعية ، وإلا فإن المستخدمين سوف يبحثون عن أسباب للتطبيق بشكل غير صحيح. ولكن من الممكن القيام بذلك باستخدام معلمة إضافية من دالة
bind
. ستكون هذه الوظيفة مفيدة ، على سبيل المثال ، لتنظيم الاختبار.
val kodein = Kodein { bind<Api>() with singleton { ApiImpl() } bind<Api>(overrides = true) with singleton { OtherApiImpl() } }
افتراضيًا ، لا يمكن للوحدات النمطية وتوابعها تجاوز التبعيات المعلنة بالفعل في كائن
Kodein
، ولكن عند استيراد وحدة نمطية ، يمكنك الإشارة إلى أن التبعيات الحالية يمكنها تجاوز التبعيات الخاصة بها ، وداخل هذه الوحدة يمكنك بالفعل تحديد التبعيات التي يمكن للآخرين تجاوزها.
لا يبدو هذا واضحًا جدًا ، فلنستخدم الأمثلة. في هذه الحالات ، سيتعطل التطبيق:
val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } import(appModule) }
val appModule = Kodein.Module("app") { bind<Gson>() with singleton { provideGson() } } val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } import(appModule, allowOverride = true) }
وفي هذا ، تبعية الوحدة النمطية الكتابة فوق التبعية تعريفها في كائن
Kodein
.
val appModule = Kodein.Module("app") { bind<Gson>(overrides = true) with singleton { provideGson() } } val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } import(appModule, allowOverride = true) }
ولكن إذا كنت تريد ذلك بالفعل وفهمت ما تفعله ، فيمكنك إنشاء وحدة نمطية ، إذا كانت هناك تبعيات متطابقة مع كائن
Kodein
ستعيد تعريفها ولن يتعطل التطبيق. نستخدم المعلمة
allowSilentOverride
للوحدة النمطية.
val testModule = Kodein.Module(name = "test", allowSilentOverride = true) { bind<EmailClient>() with singleton { MockEmailClient() } }
تناقش الوثائق المواقف الأكثر تعقيدًا مع الميراث وإعادة تعريف التبعيات ، وكذلك مع نسخ التبعيات في الورثة ، ولكن لن يتم النظر في هذه المواقف هنا.
استرجاع وحقن التبعيات
أخيرًا ، اكتشفنا كيفية الإعلان عن التبعيات بعدة طرق ، لقد حان الوقت لمعرفة كيفية الحصول عليها في فصولهم الدراسية.
Kodein
مطورو
Kodein
طريقتين للحصول على التبعيات -
injection
وإعادة
retieval
. باختصار ، يتم
injection
عندما يتلقى الفصل جميع التبعيات عند إنشائه ، أي في المنشئ ، ويكون
retrieval
عندما يكون الفصل نفسه مسؤولاً عن الحصول على التبعيات.
عند استخدام
injection
صفك لا يعرف شيئًا عن
Kodein
والرمز الموجود في الفصل الدراسي أنظف ، ولكن إذا كنت تستخدم
retrieval
،
Kodein
لك الفرصة لإدارة التبعيات بشكل أكثر مرونة. في حالة
retrieval
يتم الحصول على جميع التبعيات بتكاسل ، فقط في وقت أول نداء إلى التبعية.
طرق Kodein
لاستخدام التبعيات
يحتوي مثيل فئة
Kodein
على ثلاث طرق تُرجع تبعية أو مصنع تبعية أو موفر تبعية -
instance()
و
factory()
و
provider()
على التوالي. وبالتالي ، إذا قدمت تبعية باستخدام
factory
أو
provider
، فيمكنك أن تتلقى ليس فقط نتيجة تنفيذ الوظيفة ، ولكن أيضًا الوظيفة نفسها. تذكر أنه يمكنك استخدام العلامات في جميع الأشكال.
val kodein: Kodein = Kodein { bind<BigDecimal>() with factory { value: String -> BigDecimal(value) } bind<Random>() with provider { Random() } } private val number: BigDecimal by instance(arg = "23.87") private val numberFactory: (value: String) -> BigDecimal by factory() private val random: Random by instance() private val randomProvider: () -> Random by provider()
حقن التبعية من خلال البناء
كما فهمت بالفعل ، سيكون حول
injection
. للتنفيذ ، يجب أولاً أخذ جميع تبعيات الفصل إلى
kodein.newInstance
، ثم إنشاء مثيل للفئة عن طريق استدعاء
kodein.newInstance
class ProductApi(private val client: HttpClient, private val gson: Gson) : IProductApi class Application : Application() { val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } bind<HttpClient>() with singleton { provideHttpClient() } } private val productApi: IProductApi by kodein.newInstance { ProductApi(instance(), instance()) } }
حقن التبعية في خصائص لاغية
قد لا تعرف جيدًا ما إذا كان قد تم الإعلان عن التبعية أم لا. إذا لم يتم الإعلان عن
Kodein
مثيل
Kodein
، فسوف ينتج عن التعليمة البرمجية من المثال أعلاه
Kodein.NotFoundException
. إذا كنت ترغب في الحصول على قيمة
null
نتيجة لذلك ، إذا لم يكن هناك تبعية ، فهناك ثلاث وظائف مساعدة لهذا:
instanceOrNull()
factoryOrNull()
و
factoryOrNull()
و
providerOrNull()
.
class ProductApi(private val client: HttpClient?, private val gson: Gson) : IProductApi class Application : Application() { val kodein: Kodein = Kodein { bind<Gson>() with singleton { provideGson() } } private val productApi: IProductApi by kodein.newInstance { ProductApi(instanceOrNull(), instance()) } }
الحصول على التبعيات داخل الفصل.
كما ذكرنا سابقًا ، في حالة استخدام
retrieval
، تكون تهيئة جميع التبعيات كسولًا بشكل افتراضي. يتيح لك ذلك الحصول على التبعيات فقط عند الحاجة إليها ، والحصول على التبعيات في الفئات التي ينشئها النظام.
Activity
،
Fragment
والفئات الأخرى مع دورة حياتهم الخاصة ، كل شيء عنهم.
لتنفيذ التبعيات في
Activity
نحتاج فقط إلى رابط لمثيل Kodein ، وبعد ذلك يمكننا استخدام طرق معروفة. في الواقع ، لقد رأيت بالفعل أمثلة على
retrieval
أعلاه ، ما عليك سوى الإعلان عن عقار وتفويضه إلى إحدى الوظائف:
instance()
،
factory()
أو
provider()
private val number: BigDecimal by kodein.instance(arg = "23.87") private val numberFactory: (value: String) -> BigDecimal by kodein.factory() private val random: Random? by kodein.instanceOrNull() private val randomProvider: (() -> Random)? by kodein.providerOrNull()
تمرير المعلمات إلى المصانع
لقد رأيت بالفعل أنه من أجل تمرير معلمة إلى المصنع ، يكفي استخدام المعلمة
arg
الخاصة بوظيفة
instance
.
ولكن ماذا لو كان هناك العديد من المعلمات (قلت في وقت سابق أنه يمكن أن يكون هناك ما يصل إلى 5 معلمات في المصنع )؟ تحتاج فقط إلى تمرير arg
فصل دراسي إلى المعلمة M
التي تم تحميلها بشكل زائد على المنشئات ويمكن أن تأخذ من 2 إلى 5 وسائط. val kodein = Kodein { bind<IColorPicker>() with factory { r: Int, g: Int, b: Int, a: Int -> RgbColorPicker(r, g, b, a) } } val picker: IColorPicker by kodein.instance(arg = M(255, 211, 175, 215))
قوة التهيئة التبعية
كما قالوا - بشكل افتراضي ، التهيئة كسول ، لكن يمكنك إنشاء مشغل ، وربطه بخاصية ، أو عدة خصائص أو مثيل كامل Kodein
، بعد سحب هذا المشغل وسيتم تهيئة التبعيات. val myTrigger = KodeinTrigger() val gson: Gson by kodein.on(trigger = myTrigger).instance() myTrigger.trigger()
val myTrigger = KodeinTrigger() val kodeinWithTrigger = kodein.on(trigger = myTrigger) val gson: Gson by kodeinWithTrigger.instance() myTrigger.trigger()
إنشاء مثيل كودين كسول
قبل ذلك ، أنشأنا باستمرار مثيلًا صريحًا Kodein
، لكن من الممكن تأجيل تهيئة هذه الخاصية باستخدام فئة LazyKodein
تأخذ وظيفة في المُنشئ والتي يجب أن تُرجع كائنًا Kodein
.قد يكون هذا النهج مفيدًا ، على سبيل المثال ، إذا لم يكن معروفًا ما إذا كانت هناك حاجة على الإطلاق إلى تبعيات من مثيل Kodein معين. val kodein: Kodein = LazyKodein { Kodein { bind<BigDecimal>() with factory { value: String -> BigDecimal(value) } bind<Random>() with provider { Random() } } } private val number: BigDecimal by kodein.instance(arg = "13.4") number.toPlainString()
استدعاء Kodein.lazy سيؤدي إلى نتيجة مماثلة. val kodein: Kodein = Kodein.lazy { bind<BigDecimal>() with factory { value: String -> BigDecimal(value) } bind<Random>() with provider { Random() } } private val number: BigDecimal by kodein.instance(arg = "13.4") number.toPlainString()
Kodein تأخر التهيئة
للتأخير المؤجل ، Kodein
يوجد كائن LateInitKodein
. يمكنك إنشاء هذا الكائن ، وتفويض إنشاء الخصائص إليه ، وبعد تهيئة الكائن نفسه ، قم بتعيين الخاصية عليه baseKodein
، وبعد ذلك يمكنك بالفعل الوصول إلى التبعيات. val kodein = LateInitKodein() val gson: Gson by kodein.instance() kodein.baseKodein = gson.fromJson(someStr)
الحصول على جميع مثيلات النوع المحدد
يمكنك أن تسأل Kodein عن مثيل من النوع المحدد وجميع أحفاده في النموذج List
. كل شيء فقط داخل العلامة المحددة. للقيام بذلك، هناك طرق allInstances
، allProviders
، allFactories
. val kodein: Kodein = Kodein { bind<Number>() with singleton { Short.MAX_VALUE } bind<Double>() with singleton { 12.46 } bind<Double>("someTag") with singleton { 43.89 } bind<Int>() with singleton { 4562 } bind<Float>() with singleton { 136.88f } } val numbers: List<Number> by kodein.allInstances()
إذا قمت بالطباعة إلى السجل ، فسترى هناك [32767 ، 136.88 ، 4562 ، 12.46]. التبعية مع العلامة ليست في القائمة.تبسيط اكتساب التبعية باستخدام واجهة KodeinAware
تلزمك هذه الواجهة بتجاوز خاصية الكتابة Kodein
، وفي المقابل توفر الوصول إلى جميع الوظائف المتاحة للمثيل Kodein
. class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<Number>() with singleton { Short.MAX_VALUE } bind<Double>() with singleton { 12.46 } bind<Double>("someTag") with singleton { 43.89 } bind<Int>() with singleton { 4562 } bind<Float>() with singleton { 136.88f } } val numbers: List<Number> by allInstances() }
كما ترون ، الآن يمكنك ببساطة الكتابة by allInstances()
بدلاً من by kodein.allInstances()
السابق ، لقد تحدثنا بالفعل عن المشغل لتلقي التبعيات. في الواجهة ، KodeinAware
يمكنك تجاوز المشغل والحصول على جميع التبعيات المعلنة عند استدعاء هذا المشغل. class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<Number>() with singleton { Short.MAX_VALUE } bind<Double>() with singleton { 12.46 } bind<Double>("someTag") with singleton { 43.89 } bind<Int>() with singleton { 4562 } bind<Float>() with singleton { 136.88f } } override val kodeinTrigger = KodeinTrigger() val numbers: List<Number> by allInstances() override fun onCreate() { super.onCreate() kodeinTrigger.trigger() } }
نظرًا لأن الوصول إلى التبعيات Kodein
والمثيل كسول ، يمكنك تفويض تهيئة المثيل إلى Kodein
الوظيفة المدمجة في Kotlin lazy
. قد يكون هذا النهج مفيدًا في الفئات وفقًا لسياقها ، على سبيل المثال ، في Activity
. class CategoriesActivity : Activity(), KodeinAware { override val kodein: Kodein by lazy { (application as MyApplication).kodein } private val myFloat: Float by instance()
لنفس الأسباب ، يمكنك استخدام معدل lateinit
. class CategoriesActivity : Activity(), KodeinAware { override lateinit var kodein: Kodein private val myFloat: Float by instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) kodein = (application as MyApplication).kodein }
الوصول إلى التبعيات دون تفويض الخصائص
إذا كنت لا تريد استخدام تفويض الممتلكات لسبب ما ، يمكنك استخدام الوصول المباشر من خلال DKodein
(من مباشرة). الفرق الرئيسي هو أنه لن يكون هناك تهيئة كسولة بعد الآن ، وسيتم الحصول على التبعية فورًا في وقت الاتصال instance
، provider
ووظائف مماثلة. يمكنك الحصول DKodein
عليه من مثيل Kodein الحالي أو الإنشاء من البداية. class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<BigDecimal>() with singleton { BigDecimal.TEN } } val directKodein: DKodein = kodein.direct val directKodein2: DKodein = Kodein.direct { bind<BigDecimal>() with singleton { BigDecimal.ONE } } val someNumber:BigDecimal = directKodein.instance() val someNumber2:BigDecimal = directKodein2.instance()
Kodein يمكن استخدامها في الإطار KodeinAware
، وفي DKodein
الإطار DKodeinAware
، يمكنك تجربة.الحصول على التبعيات في أي سياق
من أجل الحصول على Kodein
العديد من التبعيات من نفس النوع من كائن واحد ، قمنا بالفعل بفحص خيار استخدام العلامات والمصانع ذات الوسيطات ، ولكن هناك شيء آخر - لاستخدام السياق (وهذا ليس السياق الموجود في Android).الاختلافات من التبعية مع العلامة:- لا يمكن استخدام العلامة داخل دالة ننشئ فيها تبعية
- عند استخدام السياق ، يمكننا الوصول إلى مثيل السياق في وظيفة إنشاء التبعية
في كثير من الأحيان ، بدلاً من السياق ، يمكنك استخدام مصنع ذي وسيطة ، Kodein
ويوصي المطورون بالقيام بذلك إذا لم تكن متأكدًا مما يجب استخدامه. لكن يمكن أن يكون السياق مفيدًا ، على سبيل المثال ، عندما لا تتمكن من طرح وسيطين على نفس النوع.على سبيل المثال، هل لديك Activity
و Presenter
، على النحو الذي تريد، وذلك باستخدام كائن واحد Kodein
، لتوفير عدة أنواع مختلفة من تبعيات بطرق مختلفة، اعتمادا على الدرجة التي يتم الحصول عليها. لقيادة Activity
و Presenter
لنوع واحد - تحتاج إلى واجهة اختيارية، وسوف مصنع لها للتحقق من نوع حجة الناتجة عن ذلك. مخطط ليست مريحة للغاية. لذلك ، نحن ننظر في كيفية استخدام السياق: class MyApplication : Application(), KodeinAware { override val kodein: Kodein = Kodein { bind<BigDecimal>() with contexted<CategoriesActivity>().provider { context.getActivityBigDecimal() } bind<BigDecimal>() with contexted<CategoriesPresenter>().factory { initialValue:BigDecimal -> context.getPresenterBigDecimal(initialValue) } } } class CategoriesActivity : Activity(), AppKodeinAware { fun getActivityBigDecimal() = BigDecimal("16.34") private val activityBigDecimal: BigDecimal by kodein.on(context = this).instance() } class CategoriesPresenter : AppKodeinAware { fun getPresenterBigDecimal(initialValue: BigDecimal) = initialValue * BigDecimal.TEN private val presenterBigDecimal: BigDecimal by kodein.on(context = this).instance(arg = BigDecimal("31.74")) }
على سبيل المثال ، سيتم سحب مثال على الأذنين وفي الممارسة العملية ، من غير المحتمل أن تواجه مثل هذا الموقف ، لكن هذا المثال يوضح كيفية عمل السياق.لإعلان التبعية ، لا تحددها with provider()
، ولكن with contexted<OurContextClass>().provider
، أين OurContextClass
هو نوع الفئة ، التي ستعمل مثيل لها كسياق. contexted
يمكن أن يكون مزود فقط أو المصنع.يتم الوصول إلى هذا السياق في الوظيفة التي تقوم بإرجاع التبعية من خلال متغير مسمى context
.للحصول على تبعية مرتبطة بسياق ، تحتاج أولاً إلى تحديد سياق الكائن من Kodein
خلال الوظيفة on()
، ثم طلب التبعية.وبالمثل ، يتم استخدام السياق في حالة injection
. private val productApi: IProductApi by kodein.on(context = someContext).newInstance { ProductApi(instance(), instance()) } }
ملحقات أندرويد
في بداية المقال ، وعدت بالنظر في خيارات التوسع ل Android
.لا شيء يمنعك من استخدامه Kodein
كما ناقشنا أعلاه ، ولكن يمكنك جعل كل شيء من حيث الحجم أكثر ملاءمة.المدمج في Kodein لالروبوت
شيء مفيد للغاية هو وحدة أعدت لنظام أندرويد. لتوصيله ، من الضروري أن يقوم الفصل Application
بتنفيذ KodeinAware
وتهيئة الخاصية Kodein
بتكاسل (للوصول إلى المثيل Application
). في المقابل ، تحصل على عدد كبير من التبعيات المعلنة التي يمكنك الحصول عليها من الفصل Application
، بما في ذلك كل ما تحتاجه Context
. كيفية الاتصال - إلقاء نظرة على مثال. class MyApplication : Application(), KodeinAware { override val kodein = Kodein.lazy { import(androidModule(this@MyApplication))
كما ترون - يمكن أن تحصل، على سبيل المثال LayoutInflater
. للحصول على قائمة كاملة بالتبعيات المعلنة في الوحدة - انظر هنا .إذا كنت ترغب في الحصول على هذه التبعيات خارج فئات Android التي تدرك سياقها ، فحدد السياق بشكل صريح. val inflater: LayoutInflater by kodein.on(context = getActivity()).instance()
الحصول بسرعة Kodein الأصل من خلال closestKodein ()
الأمر بسيط ، في Android ، تعتمد بعض الكائنات على أشياء أخرى. في المستوى الأعلى ، يوجد تطبيق ، يكون تحته النشاط ثم الشظية. يمكنك التنفيذ في النشاط KodeinAware
، وكتهيئة ، اتصل closestKodein()
ومن ثم احصل على مثيل Kodein
من Application
. class MyActivity : Activity(), KodeinAware { override val kodein by closestKodein() val ds: DataSource by instance() }
closestKodein
يمكنك أيضًا الحصول عليها خارج فئات Android ، لكنك تحتاج إلى سياق Android يمكنك من خلاله استدعاء الوظيفة. إذا كنت تستخدمها KodeinAware
، فحدد سياقها أيضًا (تجاوز الخاصية المطابقة وقم بتمرير سياق Android إلى الوظيفة kcontext()
). class MyController(androidContext: Context) : KodeinAware { override val kodein by androidContext.closestKodein() override val kodeinContext = kcontext(androidContext) val inflater: LayoutInflater by instance() }
إنشاء Kodein منفصلة في النشاط
قد يكون من الضروري أن ترث من Kodein الأصل في النشاط وتوسيعه. الحل بسيط جدا. class MyActivity : Activity(), KodeinAware { private val parentKodein by closestKodein() override val kodein: Kodein by Kodein.lazy { extend(parentKodein) } }
Kodein الذي يخضع لتغيير التكوين
نعم تستطيع. هناك وظيفة لهذا الغرض retainedKodein
. عند استخدامه ، Kodein
لن يتم إعادة إنشاء الكائن بعد تغيير التكوين. class MyActivity : Activity(), KodeinAware { private val parentKodein by closestKodein() override val kodein: Kodein by retainedKodein { extend(parentKodein) } }
ما لا يقال في المقال؟
لم أكن أدعي أنها كاملة ، وأنا شخصياً لا أفهم بعض الأشياء جيدًا بما يكفي لمحاولة توضيحها. فيما يلي قائمة بما يمكنك تعلمه بنفسك ، مع معرفة المبادئ الأساسية:- النطاقات
- ملزم المثال
- متعدد ملزمة
- النداءات onready
- مصدر خارجي
- نسخة محوها المزالق
- شكلي Kodein
- JSR-330 التوافق
حسنا وصلات إلى الوثائق:شكرا للقراءة ، آمل أن تكون المقالة مفيدة لك!