ما مِن استراتيجية تقسيم إلى وحدات واحدة تناسب جميع المشاريع. بسبب والطبيعة المرنة لنظام Gradle، فهناك القليل من القيود حول كيفية لتنظيم مشروع ما. تقدّم هذه الصفحة نظرة عامة على بعض القواعد العامة والنقاط الشائعة التي يمكنك استخدامها عند تطوير تطبيقات Android متعددة الوحدات.
ترابط عالٍ ومبدأ الاقتران المنخفض
إحدى طرق وصف قاعدة الرموز النموذجية هي استخدام أداة الاقتران والترابط. يعمل الاقتران على قياس مدى توافق الوحدات يعتمدون على بعضهم البعض. في هذا السياق، يقيس التماسك كيفية تأثير عناصر وحدة واحدة مرتبطة وظيفيًا. كقاعدة عامة، يجب أن تسعى إلى اقتران منخفض وترابط عالٍ:
- يعني الاقتران المنخفض أنه يجب أن تكون الوحدات مستقلة قدر الإمكان عن بعضها البعض، بحيث يكون للتغييرات التي يتم إجراؤها على وحدة ما تأثير صفري أو بسيط على والوحدات الأخرى. يجب ألا تتضمّن الوحدات معلومات عن الأعمال الداخلية الوحدات الأخرى.
- يعني التماسك العالي أن الوحدات يجب أن تشتمل على مجموعة من الرموز البرمجية يعمل كنظام. ينبغي أن يكون لديهم مسئوليات محددة بوضوح وأن يظلوا أن تكون ضمن حدود بعض المعرفة بالمجال. الاطّلاع على نموذج كتاب إلكتروني التطبيق. قد يكون من غير الملائم الجمع بين رمز مرتبط بالحجز والدفع. معًا في نفس الوحدة حيث إنهما مجالان وظيفيان مختلفان.
أنواع الوحدات
تعتمد طريقة تنظيم الوحدات بشكل أساسي على بنية تطبيقك. أقل من هي بعض الأنواع الشائعة من الوحدات التي يمكنك تقديمها في تطبيقك أثناء بنية التطبيق المقترَحة.
وحدات البيانات
عادة ما تحتوي وحدة البيانات على مستودع ومصادر بيانات وفئات نماذج. تشير رسالة الأشكال البيانية هناك ثلاث مسئوليات أساسية لوحدة البيانات وهي:
- تضمين جميع البيانات ومنطق الأعمال لنطاق معين: يتضمن كل نوع بيانات مسئولة عن التعامل مع البيانات التي تمثل مقدارًا محددًا مجالك. حيث يمكنها معالجة العديد من أنواع البيانات طالما أنها مرتبطة ببعضها.
- عرض المستودع كواجهة برمجة تطبيقات خارجية: واجهة برمجة تطبيقات عامة للبيانات مستودعًا حيث تكون مسئولة عن كشف البيانات بقية التطبيق.
- إخفاء جميع تفاصيل التنفيذ ومصادر البيانات من الخارج:
يجب ألا تكون مصادر البيانات متاحة إلا من خلال المستودعات من نفس الوحدة.
وتظل مخفية عن الخارج. ويمكنك فرض ذلك باستخدام خوارزميات Kotlin
الكلمة الرئيسية
private
أوinternal
لمستوى الرؤية.
![](https://developer.android.google.cn/static/topic/modularization/images/2_data_modules.png?authuser=19&hl=ar)
وحدات الميزات
الميزة هي جزء معزول من وظيفة التطبيق التي تتوافق عادةً إلى شاشة أو سلسلة من الشاشات وثيقة الصلة، مثل اشتراك أو دفع التدفق. إذا كان تطبيقك يحتوي على شريط تنقُّل سفلي، من المحتمل أن تتضمّن كل وجهة هي ميزة.
![](https://developer.android.google.cn/static/topic/modularization/images/2_bottom_bar.png?authuser=19&hl=ar)
يتم ربط الميزات بالشاشات أو الوجهات في تطبيقك. ولذلك،
من المحتمل أن تكون هناك واجهة مستخدم مرتبطة به وViewModel
لالتعامل مع منطقه
والولاية. لا يجب أن تقتصر ميزة واحدة على مشاهدة واحدة
وجهة التنقل. تعتمد وحدات الميزات على وحدات البيانات.
![](https://developer.android.google.cn/static/topic/modularization/images/2_feature_modules.png?authuser=19&hl=ar)
وحدات التطبيق
وحدات التطبيق هي نقطة دخول إلى التطبيق. تعتمد على الميزة وتوفر عادةً التنقل الجذر. يمكن تجميع وحدة تطبيق واحدة إلى عدد من البرامج الثنائية المختلفة بفضل الإصدارات المختلفة.
![](https://developer.android.google.cn/static/topic/modularization/images/2_demo_full_dep_graph.png?authuser=19&hl=ar)
إذا كان تطبيقك يستهدف أنواعًا متعددة من الأجهزة، مثل السيارات أو Wear أو التلفزيون، حدِّد وحدة تطبيق لكل نوع. ويساعد ذلك في فصل المنصات والتبعيات لديك.
![](https://developer.android.google.cn/static/topic/modularization/images/2_wear_dep_graph.png?authuser=19&hl=ar)
الوحدات الشائعة
تحتوي الوحدات الشائعة، المعروفة أيضًا بالوحدات الأساسية، على رموز برمجية تستخدمها الوحدات الأخرى استخدامها بشكل متكرر. إنها تقلل التكرار ولا تمثل أي طبقة محددة في بنية التطبيق. في ما يلي أمثلة على الوحدات الشائعة:
- وحدة واجهة المستخدم: إذا كنت تستخدم عناصر واجهة مستخدم مخصصة أو وضعت علامة تجارية محسّنة في يجب أن تفكر في تغليف مجموعة الأدوات الخاصة بك في وحدة لإعادة استخدام جميع الميزات. وقد يساعد ذلك في جعل واجهة المستخدم متسقة عبر ميزات مختلفة. على سبيل المثال، إذا كانت موضوعاتك مركزية، فيمكنك تجنب عملية إعادة هيكلة مؤلمة عند حدوث تغيير للعلامة التجارية.
- وحدة "إحصاءات Google": غالبًا ما يكون التتبّع مستندًا إلى متطلبات النشاط التجاري مع الاهتمام قليلاً بهندسة البرامج. تُعد أدوات تتبع التحليلات تُستخدم غالبًا في العديد من المكونات غير ذات الصلة. إذا كان هذا هو الحال بالنسبة لك، فقد سيكون من الجيد الحصول على وحدة تحليلات مخصصة.
- وحدة الشبكة: عندما تتطلب العديد من الوحدات اتصالاً بالشبكة، يمكنك إجراء يمكنك امتلاك وحدة مخصصة لتوفير عميل http. من المهم عندما يتطلب برنامجك تهيئة مخصصة.
- وحدة الخدمات: تكون عادةً المرافق، المعروفة أيضًا بأدوات المساعدة، قطعًا صغيرة. من الرمز البرمجي يُعاد استخدامه في التطبيق تشمل أمثلة المرافق أدوات مساعدة الاختبار أو دالة تنسيق العملات أو مدقق البريد الإلكتروني أو أداة .
وحدات الاختبار
وحدات الاختبار هي وحدات Android يتم استخدامها لأغراض الاختبار فقط. وتحتوي الوحدات على رمز اختبار وموارد اختبار وتبعيات اختبارية مطلوبة لإجراء الاختبارات وهي غير مطلوبة أثناء وقت تشغيل التطبيق. يتم إنشاء وحدات الاختبار لفصل الكود الخاص بالاختبار عن الرمز التطبيق، مما يجعل إدارة كود الوحدة أسهل وصيانته.
حالات الاستخدام لوحدات الاختبار
تعرض الأمثلة التالية الحالات التي يتم فيها تنفيذ وحدات الاختبار مفيدة بشكل خاص:
رمز الاختبار المشترك: إذا كان لديك وحدات متعددة في مشروعك وبعض تطبيق رمز الاختبار على أكثر من وحدة، يمكنك إنشاء اختبار لمشاركة التعليمة البرمجية. يمكن أن يساعد ذلك في تقليل التكرار وتسهيل إجراء الاختبار أسهل في صيانتها. يمكن أن يتضمن رمز الاختبار المشترك فئات المرافق أو الدوال، مثل التأكيدات المخصصة أو المطابقات، بالإضافة إلى بيانات الاختبار، مثل وتم محاكاة استجابات JSON.
إعدادات تصميم أكثر سلاسة: تتيح لك وحدات الاختبار الحصول على إنشاء تهيئات، إذ يمكن أن يكون لها ملف
build.gradle
الخاص بها. لا تفعل إلى تشويش ملفbuild.gradle
في وحدة تطبيقك بالإعدادات التالية: المناسبة للاختبارات فقط.اختبارات الدمج: يمكن استخدام وحدات الاختبار لتخزين عملية الدمج. الاختبارات المستخدَمة لاختبار التفاعلات بين أجزاء مختلفة من تطبيقك بما في ذلك واجهة المستخدم ومنطق الأعمال وطلبات الشبكة واستعلامات قاعدة البيانات.
التطبيقات الواسعة النطاق: تكون وحدات الاختبار مفيدة بشكل خاص في تطبيقات واسعة النطاق ذات قواعد رموز معقّدة ووحدات متعددة. في مثل هذه في الحالات، يمكن لوحدات الاختبار المساعدة في تحسين تنظيم الرموز البرمجية وصيانتها.
![](https://developer.android.google.cn/static/topic/modularization/images/2_test_modules.png?authuser=19&hl=ar)
وحدة التواصل
نادرًا ما توجد الوحدات في الفصل الكلي وغالبًا ما تعتمد على الوحدات التواصل معهم. من المهم الحفاظ على الاقتران منخفض حتى عندما تكون الوحدات معًا ويتبادلون المعلومات بشكل متكرر. الزيارات المباشرة أحيانًا فإن الاتصال بين وحدتين إما ليس مرغوبًا فيه كما هو الحال في القيود الهندسية. وقد يكون من المستحيل أيضًا، كما هو الحال مع التحويل الدوري والتبعيات لديك.
![](https://developer.android.google.cn/static/topic/modularization/images/2_mediator.png?authuser=19&hl=ar)
للتغلب على هذه المشكلة، يمكنك استخدام وحدة ثالثة للتوسّط. بين وحدتين أخريين. يمكن لوحدة الوسيط الاستماع إلى الرسائل الواردة من كليهما الوحدات وإعادة توجيهها حسب الحاجة. في نموذج التطبيق، تشير عملية الدفع تحتاج الشاشة إلى معرفة الكتاب الذي سيتم شراؤه على الرغم من أن الحدث نشأ فيه شاشة منفصلة تشكل جزءًا من ميزة مختلفة. في هذه الحالة، الوسيط هو الوحدة التي تمتلك الرسم البياني للتنقل (عادةً ما تكون وحدة تطبيق). في المثال، نستخدم ميزة التنقّل لتمرير البيانات من الميزة الرئيسية إلى ميزة الدفع باستخدام عنصر التنقل.
navController.navigate("checkout/$bookId")
تتلقّى وجهة الدفع معرّف كتاب كوسيطة تستخدمها
لجلب معلومات حول الكتاب. يمكنك استخدام مؤشر الحالة المحفوظة من أجل
استرداد وسيطات التنقل داخل ViewModel
لعنصر وجهة.
class CheckoutViewModel(savedStateHandle: SavedStateHandle, …) : ViewModel() {
val uiState: StateFlow<CheckoutUiState> =
savedStateHandle.getStateFlow<String>("bookId", "").map { bookId ->
// produce UI state calling bookRepository.getBook(bookId)
}
…
}
ويجب عدم تمرير العناصر كوسيطات تنقُّل. بدلاً من ذلك، استخدِم معرّفات بسيطة التي يمكن للميزات استخدامها للوصول إلى الموارد المطلوبة وتحميلها من طبقة البيانات. بهذه الطريقة، تحافظ على علاقة شرارة الحبل ولا تنتهك مصدر الحقيقة الوحيد. المبدأ.
في المثال أدناه، تعتمد وحدتا الميزات على وحدة البيانات نفسها. هذا النمط من الممكن تقليل كمية البيانات التي تحتاجها وحدة الوسيط إعادة التوجيه والحفاظ على الاقتران بين الوحدتين. بدلاً من المرور يجب أن تتبادل الوحدات المعرفات الأولية وتحميل الموارد من وحدة البيانات المشتركة.
![](https://developer.android.google.cn/static/topic/modularization/images/2_shared_data.png?authuser=19&hl=ar)
عكس التبعية
عكس التبعية هو عندما تقوم بتنظيم التعليمات البرمجية الخاصة بك بحيث يكون التجريد منفصلة عن التنفيذ الملموس.
- التجريد: عقد يحدد كيفية عمل المكوّنات أو الوحدات في والتطبيق مع بعضها البعض. وحدات التجريد تحدد واجهة برمجة التطبيقات نظامك وتحتوي على واجهات ونماذج.
- التنفيذ الملموس: الوحدات التي تعتمد على وحدة التجريد تطبيق سلوك التجريد وتنفيذه.
يجب أن تقتصر الوحدات التي تعتمد على السلوك المحدّد في وحدة التجريد تعتمد على التجريد نفسه، بدلاً من عمليات التنفيذ المحددة.
![](https://developer.android.google.cn/static/topic/modularization/images/2_di_concept.png?authuser=19&hl=ar)
مثال
تخيل أن وحدة ميزة تحتاج إلى قاعدة بيانات لتعمل. وحدة الميزات ليست المعنية بكيفية تنفيذ قاعدة البيانات، سواء كانت قاعدة بيانات غرف محلية أو مثيل Firestore البعيد. يحتاج فقط إلى تخزين بيانات التطبيق وقراءتها.
ولتحقيق ذلك، تعتمد وحدة الميزات على وحدة التجريد بدلاً من في تنفيذ قاعدة بيانات محددة يحدد هذا التجريد واجهة برمجة التطبيقات لقاعدة البيانات. أو بعبارةٍ أخرى، فإنها تضع القواعد الخاصة بكيفية التفاعل مع قاعدة البيانات. وهذا يتيح لوحدة الميزة استخدام أي قاعدة بيانات دون الحاجة إلى معرفة تفاصيل التنفيذ الأساسية.
توفر وحدة التنفيذ الملموسة التنفيذ الفعلي واجهات برمجة التطبيقات المحددة في وحدة التجريد. من أجل القيام بذلك، ينبغي تنفيذ على وحدة التجريد.
إدخال التبعية
قد تتساءل الآن كيف ترتبط وحدة الميزات جديدة. الإجابة هي Dependency Injection. الميزة لا تنشئ بشكل مباشر مثيل قاعدة البيانات المطلوب. بدلاً من ذلك، التبعيات التي يحتاجها. ثم يتم توفير هذه التبعيات خارجيًا، عادةً في وحدة التطبيق.
releaseImplementation(project(":database:impl:firestore"))
debugImplementation(project(":database:impl:room"))
androidTestImplementation(project(":database:impl:mock"))
المزايا
في ما يلي فوائد فصل واجهات برمجة التطبيقات وعمليات تنفيذها:
- قابلية التبادل: من خلال الفصل بوضوح بين واجهة برمجة التطبيقات والتنفيذ يمكنك تطوير عمليات تنفيذ متعددة لواجهة برمجة التطبيقات نفسها، وتبديل بينهما بدون تغيير الرمز الذي يستخدم واجهة برمجة التطبيقات. يمكن أن تظهر فائدة هذا بوضوح أكبر مفيدة بشكل خاص في السيناريوهات التي تريد فيها تقديم القدرات أو السلوك في سياقات مختلفة. على سبيل المثال، نموذج تجريبي التنفيذ للاختبار مقابل التنفيذ الحقيقي للإنتاج.
- فصل الاقتران: يعني الفصل أن الوحدات التي تستخدم التجريدات لا تستخدم تعتمد على أي تقنية محددة. إذا اخترت تغيير قاعدة البيانات من من غرفة إلى Firestore لاحقًا، سيكون الأمر أسهل لأن التغييرات لن في الوحدة المحددة التي تؤدي المهمة (وحدة التنفيذ)، ولن يكون لها تؤثر على الوحدات الأخرى باستخدام واجهة برمجة التطبيقات لقاعدة بياناتك.
- قابلية الاختبار: يمكن أن يؤدي فصل واجهات برمجة التطبيقات عن عمليات تنفيذها إلى حد كبير وتسهيل الاختبار. يمكنك كتابة حالات اختبار مقابل عقود واجهات برمجة التطبيقات. يمكنك أيضًا استخدام عمليات تنفيذ مختلفة لاختبار سيناريوهات وحالات حدية مختلفة، بما في ذلك عمليات تنفيذ وهمية.
- تحسين أداء الإصدار: عند فصل واجهة برمجة التطبيقات عن واجهة برمجة التطبيقات والتنفيذ في وحدات مختلفة، والتغييرات في التنفيذ لا تجبر نظام التصميم على إعادة تجميع الوحدات اعتمادًا على وحدة واجهة برمجة التطبيقات. ويؤدي ذلك إلى زيادة سرعة التصميم وزيادة الإنتاجية خاصة في المشروعات الكبيرة حيث يمكن أن تكون أوقات الإنشاء كبيرة.
متى يتم الفصل
من المفيد فصل واجهات برمجة التطبيقات عن عمليات تنفيذها في في الحالات التالية:
- إمكانات متنوعة: إذا كان بإمكانك تنفيذ أجزاء من نظامك في بطرق متعددة، تتيح واجهة برمجة التطبيقات الواضحة إمكانية التبادل وعمليات التنفيذ. على سبيل المثال، قد يكون لديك نظام عرض يستخدم OpenGL أو Vulkan أو نظام فوترة يعمل مع Play أو نظام الفوترة الداخلي واجهة برمجة التطبيقات.
- تطبيقات متعددة: إذا كنت تطوِّر تطبيقات متعددة باستخدام والإمكانات المشتركة للأنظمة الأساسية المختلفة، يمكنك تحديد واجهات برمجة التطبيقات وتطوير عمليات تنفيذ محددة لكل منصة.
- الفرق المستقلة: يتيح الفصل لمختلف المطورين أو الفرق ويعملان على أجزاء مختلفة من قاعدة الرموز في آن واحد. على المطوّرين التركيز على فهم عقود واجهة برمجة التطبيقات واستخدامها بشكل صحيح. لا يحتاج إلى تتعلق بتفاصيل التنفيذ للوحدات الأخرى.
- قاعدة رموز كبيرة: عندما تكون قاعدة الرموز كبيرة أو معقّدة، يفصل بين واجهة برمجة التطبيقات من التنفيذ يجعل الرمز أكثر قابلية للإدارة. إنه يتيح لك كسر إلى وحدات أكثر دقة وسهلة الفهم وقابلة للصيانة.
كيفية التنفيذ
لتنفيذ عكس التبعية، اتبع الخطوات التالية:
- إنشاء وحدة تجريد: يجب أن تحتوي هذه الوحدة على واجهات برمجة تطبيقات (واجهات). التي تحدد سلوك الميزة لديك.
- إنشاء وحدات تنفيذ: يجب أن تعتمد وحدات التنفيذ على
وحدة واجهة برمجة التطبيقات وتنفيذ سلوك التجريد.
الشكل 10. تعتمد وحدات التنفيذ على وحدة التجريد. - إنشاء وحدات عالية المستوى تعتمد على وحدات التجريد: بدلاً من
اعتمادًا على طريقة التنفيذ المحددة، اجعل الوحدات تعتمد على
التجريد. إنّ الوحدات ذات المستوى العالي لا تحتاج إلى معرفة آلية التنفيذ.
التفاصيل، فهم بحاجة فقط إلى العقد (واجهة برمجة التطبيقات).
الشكل 11. تعتمد الوحدات عالية المستوى على التجريدات وليس على التنفيذ. - تقديم وحدة تنفيذ: أخيرًا، عليك توفير البيانات الفعلية
التنفيذ لتبعياتك. تعتمد طريقة التنفيذ المحددة على
إعداد مشروعك، ولكن عادةً ما تكون وحدة التطبيق مكانًا جيدًا للقيام بذلك.
لتوفير عملية التنفيذ، حددها كتبعية للجدول الزمني الذي اخترته
صيغة الإصدار أو مجموعة مصادر اختبار.
الشكل 12. توفّر وحدة التطبيق عملية التنفيذ الفعلي.
أفضل الممارسات العامة
وكما ذكرنا في البداية، لا توجد طريقة واحدة صحيحة لتطوير تطبيق متعدد الوحدات. تمامًا مثل وجود العديد من هياكل البرامج، توجد طرق عديدة لتقسيم التطبيق إلى وحدات. ومع ذلك، فإن العناصر العامة التالية في جعل التعليمة البرمجية أكثر سهولة في القراءة وقابلية الصيانة للاختبار.
الحفاظ على اتساق الإعدادات
تقدم كل وحدة الزيادة في الإعدادات. إذا زاد عدد الوحدات حد معين، فإن إدارة التكوين المتسق تصبح معين. على سبيل المثال، من المهم أن تستخدم الوحدات تبعيات من نفس . إذا كنت بحاجة إلى تحديث عدد كبير من الوحدات لمجرد نقل التبعية، فهي لا تمثل جهدًا فحسب، بل تمثل أيضًا مجالاً لإمكانات الأخطاء. ولحل هذه المشكلة، يمكنك استخدام إحدى أدوات غرادل مركزية التكوين الخاص بك:
- كتالوجات الإصدارات هي قائمة آمنة من التبعيات التي أنشأتها Gradle أثناء المزامنة. وهو مكان مركزي للإعلان عن جميع والاعتمادية وهو متوفر لجميع الوحدات في المشروع.
- استخدِم المكوّنات الإضافية للاصطلاحات لمشاركة منطق الإصدار بين الوحدات.
إظهار أقل قدر ممكن
يجب أن تكون الواجهة العامة للوحدة بسيطة وأن تعرض
والأساسيات. يجب عدم تسريب أي تفاصيل تنفيذ إلى الخارج. النطاق
من كل شيء إلى أصغر قدر ممكن. استخدام private
أو internal
من Kotlin
نطاق الرؤية لجعل وحدة التعريفات خاصة. عند الإعلان
التابعة في وحدتك، حيث تفضل implementation
على api
. الخيار الأخير
يعرض التبعيات المنتقلة لمستخدمي الوحدة. استخدام
قد يؤدي تنفيذ هذه العمليات إلى تحسين وقت الإصدار لأنّها تقلل من عدد الوحدات
التي يجب إعادة بنائها.
تفضيل Kotlin وحدات Java
تتوفّر ثلاثة أنواع أساسية من الوحدات المتوافقة مع "استوديو Android":
- وحدات التطبيقات هي نقطة دخول إلى تطبيقك. يمكن أن تحتوي على
رمز المصدر والموارد ومواد العرض و
AndroidManifest.xml
. ناتج وحدة التطبيق هي تنسيق "مجموعة حزمات تطبيق Android" (AAB) أو حزمة تطبيقات Android ملف APK. - تتضمّن وحدات المكتبة المحتوى نفسه الذي تتضمّنه وحدات التطبيق. وهي تستخدمها وحدات Android الأخرى كاعتمادية. ناتج وحدة المكتبة هو أرشيف Android (AAR) متطابق هيكليًا مع وحدات التطبيق، يتم تجميعها في ملف أرشيف Android (AAR) والذي يمكن استخدامه لاحقًا من خلال الوحدات باعتبارها تبعية. تتيح وحدة المكتبة التي تتضمّن نفس المنطق والموارد وتعيد استخدامها في العديد من وحدات التطبيق.
- لا تحتوي مكتبات Kotlin وJava على أي موارد أو مواد عرض أو ملفات البيان.
بما أنّ وحدات Android تأتي بنفقات إضافية، ويفضَّل ذلك، ننصحك باستخدام نوع Kotlin أو Java قدر الإمكان.