لماذا نُجزّئ التطبيق إلى وحدات
سرعة البناء والملكية وإعادة الاستخدام
لماذا نُجزّئ التطبيق إلى وحدات درس مجاني في Android Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Android Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Android Academy 4 دروس في المجموع.
مشكلة التطبيق أحادي الكتلة
عندما يكبر تطبيق Android، غالبًا ما تعيش شفرته كلها في وحدة app واحدة. وهذا ما يُسمى تطبيقًا أحادي الكتلة. يكون ذلك مريحًا في البداية، لكنه يصبح مؤلمًا مع الوقت: إذ تلمس كل تغييرات الوحدة نفسها، وتصبح عمليات البناء بطيئة، ويتعارض أعضاء الفريق باستمرار مع بعضهم.
تعني التجزئة إلى وحدات تقسيم تلك الوحدة الكبيرة إلى وحدات Gradle أصغر وأكثر تخصصًا. في هذا الدرس ستتعلمون سبب اتباع الفرق لهذا الأسلوب والفوائد العملية التي يحققها.
ما الوحدة فعلًا؟
في Gradle، الوحدة هي وحدة شفرة يمكن بناؤها بشكل مستقل، ولها ملف build.gradle.kts الخاص بها. يحتوي تطبيقكم بالفعل على وحدة واحدة على الأقل، وهي وحدة :app. وتُعرّفون كل وحدة في settings.gradle.kts.
إضافة وحدة بسيطة بقدر تضمينها. تنتج كل وحدة مخرجات البناء الخاصة بها، ويمكنها الاعتماد على وحدات أخرى.
// settings.gradle.kts
include(":app")
include(":core:designsystem")
include(":core:data")
include(":feature:home")
include(":feature:profile")الفائدة 1: عمليات بناء أسرع
أكبر فائدة عملية هي سرعة البناء. يستطيع Gradle بناء الوحدات بالتوازي، والأهم أنه يستطيع تخزين الوحدات مؤقتًا وتجاوزها إذا لم تتغير مدخلاتها.
إذا عدّلتم :feature:profile فقط، فسيعيد Gradle استخدام المخرجات المبنية مسبقًا لكل وحدة أخرى. أما في التطبيق أحادي الكتلة، فقد يؤدي أي تغيير إلى إعادة ترجمة التطبيق بأكمله.
- التنفيذ المتوازي عبر الوحدات
- عمليات البناء التزايدية: إعادة بناء ما تغيّر فقط
- تحسين معدلات الاستفادة من ذاكرة البناء المؤقتة والمحلية والبعيدة
// gradle.properties
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=trueالفائدة 2: حدود واضحة
تفرض الوحدات حدودًا. لا تستطيع الشفرة في وحدة ما رؤية ما تكشفه وحدة أخرى إلا بشكل مقصود. ويمنع ذلك التشابك الفوضوي الذي تصل فيه كل فئة إلى كل فئة أخرى.
تتحكمون في مستوى الرؤية باستخدام إعدادَي Gradle، api وimplementation. يبقي implementation الاعتمادية خاصة بالوحدة، فلا يستطيع المستهلكون استخدامها عن طريق الخطأ.
// feature/profile/build.gradle.kts
dependencies {
// Exposed to whoever depends on :feature:profile
api(project(":core:model"))
// Private: hidden from consumers of this module
implementation(project(":core:network"))
}الفائدة 3: إعادة الاستخدام
بمجرد وضع المنطق في وحدة متخصصة، يمكنكم إعادة استخدامه في أي مكان. ويمكن مشاركة وحدة :core:designsystem التي تحتوي على السمة والألوان والعناصر القابلة لإعادة الاستخدام مع كل ميزة.
وتتوسع الفكرة نفسها لتشمل عدة تطبيقات: إذ تستطيع الشركة مشاركة وحدة :core:network بين عدة منتجات بدلًا من نسخ الشفرة.
// Any feature can pull in shared building blocks
// feature/home/build.gradle.kts
dependencies {
implementation(project(":core:designsystem"))
implementation(project(":core:data"))
}الفائدة 4: ملكية الفريق
تتوافق الوحدات بشكل جيد مع ملكية الفرق. يمتلك فريق المدفوعات :feature:payments، ويمتلك فريق الملف الشخصي :feature:profile. ويمكن للفريقين العمل بالتوازي مع تعارضات دمج أقل، لأن شفرتهما موجودة في مجلدات وملفات بناء منفصلة.
يمكن لأدوات مثل ملف CODEOWNERS طلب المراجعات تلقائيًا من الفريق المناسب استنادًا إلى مسار الوحدة.
# .github/CODEOWNERS
/feature/payments/ @org/payments-team
/feature/profile/ @org/profile-team
/core/designsystem/ @org/platform-teamالفائدة 5: التغليف عبر مستوى الرؤية
داخل الوحدة، يصبح معدّل internal في Kotlin قويًا. تكون الفئة أو الدالة internal مرئية داخل وحدتها فقط. ولا تستطيع الوحدات الأخرى الإشارة إليها بأي شكل.
يتيح لكم ذلك كشف واجهة عامة صغيرة وإخفاء تفاصيل التنفيذ، وهو أمر يصعب فرضه في وحدة واحدة ضخمة.
// In :core:data
// Public API other modules may use
fun interface UserRepository {
suspend fun loadUser(id: String): User
}
// Hidden from other modules
internal class DefaultUserRepository(
private val api: UserApi
) : UserRepository {
override suspend fun loadUser(id: String) = api.fetch(id).toUser()
}التكلفة: بعض النفقات الإضافية
التجزئة إلى وحدات ليست مجانية. تضيف كل وحدة ملف build.gradle.kts يجب صيانته، وعليكم التفكير في الوحدة المالكة لكل جزء من الشفرة. يؤدي الإفراط في تقسيم تطبيق صغير إلى أعمال روتينية دون فائدة حقيقية.
القاعدة العامة: جزّئوا التطبيق إلى وحدات عندما تصبح أوقات البناء مؤثرة، أو عندما تتعارض الفرق، أو عندما تكون لديكم طبقات واضحة قابلة لإعادة الاستخدام. فنادرًا ما يحتاج تطبيق هواية يُطوّر في عطلة نهاية الأسبوع إلى 30 وحدة.
تخطيط نموذجي للوحدات
يقسم هيكل شائع قابل للتوسع الوحدات إلى طبقات app وfeature وcore. توصل وحدة :app كل شيء معًا، وتحتوي وحدات الميزات على الشاشات التي يتفاعل معها المستخدم، بينما تحتوي وحدات core على البنية التحتية المشتركة.
// Conceptual project tree
// app/ <- single entry point, wires features
// feature/
// home/
// profile/
// settings/
// core/
// designsystem/ <- theme + reusable composables
// data/ <- repositories
// network/ <- Retrofit/Ktor
// model/ <- shared data classesتحافظ إضافات Convention على التنظيم
عند وجود وحدات كثيرة، يصبح نسخ إعداد Gradle نفسه ولصقه في كل مكان فخًا. تستخرج الفرق الإعدادات المشتركة في إضافات Convention (داخل وحدة build-logic). ثم تطبق كل وحدة فعلية إضافة واحدة بدلًا من تكرار عشرات الأسطر.
سترون هذا النمط في تطبيقات كبيرة مفتوحة المصدر مثل Now in Android. في الوقت الحالي، يكفي أن تعرفوا الهدف: إبقاء ملفات البناء صغيرة ومتسقة.
// feature/home/build.gradle.kts
plugins {
// One convention plugin sets up Android + Compose + Kotlin
id("myapp.android.feature")
}
android { namespace = "com.myapp.feature.home" }العقلية: فكّروا في طبقات
أكثر النماذج الذهنية فائدة هو أن تتدفق الاعتماديات إلى الأسفل. تعتمد الميزات على core، ولا يعتمد core على الميزات. وتوجد وحدة :app في القمة تمامًا، وتعتمد على كل ما تحتاج إليه لتجميع التطبيق.
إذا حافظتم على هذا الاتجاه باستمرار، فسيظل مخطط وحداتكم نظيفًا، وستتجنبون الدورات التي ستستكشفونها في درس لاحق.
// Allowed: app -> feature -> core
// Forbidden: core -> feature (upward) or feature -> feature (sideways)
// app/build.gradle.kts
dependencies {
implementation(project(":feature:home"))
implementation(project(":feature:profile"))
}تحقق سريع
أي مما يلي يمثل الفائدة اليومية الأكثر وضوحًا التي تحصل عليها الفرق من تقسيم تطبيق Android المتنامي إلى وحدات؟
مراجعة: لماذا نستخدم الوحدات؟
لقد تعلمتم سبب تقسيم الفرق للتطبيق أحادي الكتلة إلى وحدات:
- عمليات بناء أسرع عبر التنفيذ المتوازي والتخزين المؤقت التزايدي
- حدود واضحة باستخدام
api/implementationوinternal - إعادة استخدام طبقات core مثل نظام التصميم والشبكة
- ملكية الفريق مع تعارضات دمج أقل
رأيتم أيضًا التكلفة، وهي ملفات البناء الإضافية، والقاعدة الذهبية: تتدفق الاعتماديات إلى الأسفل، app -> feature -> core. في الخطوة التالية، سترسمون الحدود الفعلية بين وحدات feature وcore.
الأسئلة الشائعة
هل درس «لماذا نُجزّئ التطبيق إلى وحدات» مجاني؟
نعم — نص درس «لماذا نُجزّئ التطبيق إلى وحدات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Android Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Android Academy 4 دروس في المجموع.
ماذا ستتعلم في «لماذا نُجزّئ التطبيق إلى وحدات»؟
سرعة البناء والملكية وإعادة الاستخدام تتمرن على Android Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Android Academy؟
لا تُشترط خبرة سابقة. Android Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «لماذا نُجزّئ التطبيق إلى وحدات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Android Academy هذا؟
نعم. كل درس في Android Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- لماذا نُجزّئ التطبيق إلى وحدات
- وحدات الميزات والوحدات الأساسية
- إدارة تبعيات الوحدات
- التنقّل بين الوحدات