SOLID — خمسة مبادئ للبرمجة كائنية التوجه صاغها Robert C. Martin (Uncle Bob) في أوائل العقد الأول من القرن الحادي والعشرين. وفقًا لـ DigitalOcean، 2024، SOLID ترمز إلى Single Responsibility وOpen-Closed وLiskov Substitution وInterface Segregation وDependency Inversion. تشكل هذه المبادئ أساس Clean Architecture وتُطبق في تطوير Android (MVP، MVVM، Clean Architecture) وiOS (VIPER، TCA).
الخلاصة
SOLID — اختصار ذاكري يمثل خمسة مبادئ للتصميم كائني التوجه. تم تقديم المصطلح بواسطة Robert C. Martin في مقال «Design Principles and Design Patterns» (2000) ومن ثم نشره في كتاب «Agile Software Development: Principles, Patterns, and Practices» (2002). SOLID ليس إطار عمل أو مكتبة — إنه مجموعة من الممارسات التي تجعل الكود أقل اقترانًا وأكثر قابلية للاختبار وأسهل للتغيير.
وفقًا لـ Clean Coder Blog، 2014، كل مبدأ من SOLID يحل مشكلة تصميم محددة: SRP يحارب الفئات العملاقة (God classes)، OCP يمنع التغييرات المتتالية، LSP يحمي من الوراثة غير الصحيحة، ISP يتجنب الواجهات الكبيرة، DIP يقلل الاقتران القوي. معًا يشكلون أساس Clean Architecture، الذي يُستخدم في مشاريع Android مع MVP وMVVM وMVI.
Single Responsibility Principle (SRP) — مبدأ المسؤولية الفردية. الصيغة: «يجب أن يكون للفصل سبب واحد فقط للتغيير». هذا يعني أن كل وحدة أو فصل مسؤول عن وظيفة واحدة أو كيان مجال واحد. إذا كان الفصل يدير كلاً من المستخدمين وإرسال البريد الإلكتروني — فلديه سببان للتغيير، مما ينتهك SRP.
وفقًا لـ Robert C. Martin، 2002، SRP هو أهم مبدأ وأكثرها انتهاكًا في نفس الوقت. في التطوير المحمول، غالبًا ما يُنتهك SRP في Activity/Fragment من خلال الجمع بين منطق UI والتنقل والشبكات ومنطق الأعمال. الحل هو استخراج كل طبقة في فصل منفصل: ViewModel لمنطق UI، وRepository للبيانات، وNavController للتنقل.
تأمل فصل UserManager، الذي يحمّل الملف الشخصي ويحفظ الإعدادات ويرسل البريد الإلكتروني. هذه ثلاث مسؤوليات متميزة، يجب استخراج كل منها في فصل منفصل: UserProfileRepository (التحميل)، UserSettingsStorage (الحفظ)، وEmailService (الإرسال). كود العميل (ViewModel) يستخدم الثلاثة من خلال Dependency Injection، وكل فصل يُختبر بسهولة بشكل منفصل ويتغير دون التأثير على الآخرين.
// ❌ انتهاك SRP: Activity تعرف الشبكة وقاعدة البيانات وUI
class ProfileActivity : AppCompatActivity() {
fun loadProfile() {
api.getUser() // استدعاء الشبكة
db.saveUser() // عملية قاعدة البيانات
updateUI() // تحديث UI
}
}
// ✅ SRP مطبق: الطبقات منفصلة
class ProfileViewModel : ViewModel() {
private val repo = UserRepository()
fun loadProfile() { repo.getUser() }
}
علامات انتهاك SRP: فصل يتجاوز 200 سطر، لديه طرق من مجالات مختلفة، يتغير بشكل متكرر لأسباب مختلفة. لتطوير Android، القاعدة بسيطة: Activity تتعامل فقط مع دورة حياة الشاشة، ViewModel تتعامل مع حالة UI، Repository تتعامل مع مصادر البيانات.
مبدأ SRP لا ينطبق فقط على الفصول بل أيضًا على هندسة مستوى الخدمة. كل خدمة مصغرة تتعامل مع كيان مجال واحد: UserService — المستخدمون فقط، PaymentService — المدفوعات فقط، NotificationService — الإشعارات فقط. هذا يسمح بتوسيع ونشر واختبار الخدمات بشكل مستقل. في التطبيقات المحمولة، يتجلى SRP على مستوى الخدمات المصغرة في فصل عملاء API حسب المجال.
Open-Closed Principle (OCP) — يجب أن تكون الفصول مفتوحة للتمديد (يمكن إضافة سلوك جديد) ومغلقة للتعديل (لا يتم تغيير الكود الموجود). يتحقق ذلك من خلال تعدد الأشكال والفصول المجردة والواجهات. بدلاً من إضافة if-else إلى طريقة موجودة، يتم إنشاء تنفيذ واجهة جديد.
وفقًا لـ Clean Coder Blog، 2014، OCP يعمل بشكل أفضل مع نمط Strategy. على سبيل المثال، إذا كان التطبيق يدعم طرق دفع مختلفة (Google Pay، Apple Pay، PayPal)، ليست هناك حاجة لإضافة switch-case إلى معالج الدفع. كل طريقة دفع تنفذ واجهة مشتركة PaymentGateway، ويُضاف نظام دفع جديد كفصل جديد دون تعديل الفصول الموجودة.
// ✅ OCP: مفتوح للتمديد، مغلق للتعديل
interface PaymentGateway {
fun processPayment(amount: Double): Boolean
}
class GooglePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
// نظام دفع جديد — دون تغيير الكود الموجود
class ApplePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
Liskov Substitution Principle (LSP) — مبدأ استبدال Barbara Liskov. إذا كان S نوعًا فرعيًا من T، فيمكن استبدال كائنات من نوع T بكائنات من نوع S دون تغيير خصائص البرنامج. رسميًا: يجب أن تعمل الدالة التي تستخدم فئة أساسية بشكل صحيح مع أي من فئاتها الفرعية. إذا كانت فئة فرعية ترمي استثناءً حيث لا ترميه الفئة الأساسية — فقد انتهك LSP.
وفقًا لـ Robert C. Martin، 2002، LSP هو أصعب مبدأ SOLID للفهم. المثال الكلاسيكي للانتهاك هو فصل Square الذي يرث من Rectangle. إذا كان setWidth في Square يحدد كلاً من العرض والارتفاع، فإن كود العميل الذي يتوقع سلوك Rectangle يحصل على نتيجة غير متوقعة. في التطوير المحمول، غالبًا ما يُنتهك LSP عند وراثة ViewModel — عندما تُضيف ViewModel تابعة تبعيات إجبارية.
// ❌ انتهاك LSP: Square يكسر سلوك Rectangle
open class Rectangle(open var width: Int, open var height: Int)
class Square(side: Int) : Rectangle(side, side) {
override var width
get() = super.width
set(value) { super.setBoth(value, value) }
}
Interface Segregation Principle (ISP) — لا يجب أن يعتمد العملاء على واجهات لا يستخدمونها. بدلاً من واجهة واحدة «كبيرة»، أنشئ عدة واجهات ضيقة متخصصة. إذا كان الفصل ينفذ واجهة ولكن بعض الطرق ترمي UnsupportedOperationException أو تبقى فارغة — فهذه علامة واضحة على انتهاك ISP.
وفقًا لـ DigitalOcean، 2024، ISP مهم بشكل خاص في التطوير المحمول عند تصميم ViewModel وRepository. بدلاً من واجهة UserRepository واحدة بجميع طرق CRUD، من الأفضل إنشاء QueryUserRepository (قراءة فقط) وCommandUserRepository (كتابة). عندها يعتمد عميل القراءة فقط (عنصر UI) فقط على واجهة Query ولا يعرف شيئًا عن طرق الكتابة.
// ❌ واجهة كبيرة — العميل مجبر على تنفيذ طرق غير ضرورية
interface UserOperations {
fun getUser(id: String): User
fun saveUser(user: User)
fun deleteUser(id: String)
fun exportUsers(): File
}
// ✅ ISP: واجهات مفصولة
interface UserReader { fun getUser(id: String): User }
interface UserWriter { fun saveUser(user: User) }
interface UserDeleter { fun deleteUser(id: String) }
Dependency Inversion Principle (DIP) — الوحدات عالية المستوى لا يجب أن تعتمد على وحدات منخفضة المستوى. كلاهما يجب أن يعتمد على التجريدات (الواجهات). التجريدات لا يجب أن تعتمد على التفاصيل — التفاصيل تعتمد على التجريدات. هذا ليس «حقن التبعيات» (DI)، على الرغم من أن DI هو طريقة شائعة لتنفيذ DIP.
وفقًا لـ Robert C. Martin، 2019، DIP هو أساس Clean Architecture. ViewModel (مستوى عالٍ) لا يجب أن ينشئ مباشرة مثيل RetrofitApi (تفصيل). بدلاً من ذلك، ViewModel يعتمد على واجهة UserRepository، ويتم تمرير التنفيذ الملموس UserRepositoryImpl مع Retrofit عبر المُنشئ. في Android، يُطبق DIP من خلال Hilt/Dagger أو Koin: جميع التبعيات تُقدم عبر حاوية DI.
// ✅ DIP: Module يعتمد على التجريد، وليس على التفاصيل
class UserRepositoryImpl(
private val api: UserApi, // يعتمد على الواجهة
private val db: UserDao // يعتمد على الواجهة
) : UserRepository {
override suspend fun getUser(id: String): User {
return api.fetchUser(id)
}
}
// Hilt DI: التفاصيل تُربط عبر وحدة DI
@Module
object NetworkModule {
@Provides
fun provideUserApi(retrofit: Retrofit): UserApi =
retrofit.create(UserApi::class.java)
}
SOLID في التطوير المحمول يُطبق على جميع المستويات: من هندسة التطبيق إلى الفصول الفردية. في مشاريع Android، تقسم Clean Architecture الكود إلى ثلاث طبقات: domain (منطق الأعمال — مستقل عن الأطر)، data (المستودعات، API، قاعدة البيانات)، وpresentation (UI، ViewModel). طبقة domain تستخدم مبادئ SOLID: حالات الاستخدام (SRP)، واجهات المستودع (DIP)، فصول الكيانات (OCP + LSP).
وفقًا لـ Android Developers Guide، 2025، SRP في Android يتجلى في فصل ViewModel وRepository وMapper. OCP — عند إضافة مصادر بيانات جديدة عبر واجهة DataSource. LSP — في معالجة Result الموحدة عبر المستودعات المختلفة. ISP — في نهج CQRS (فصل مستودعات القراءة/الكتابة). DIP — من خلال Hilt/Koin لحقن التبعيات.
| المبدأ | المشكلة بدونه | الحل في المشروع المحمول |
|---|---|---|
| SRP | Activity بأكثر من 1000 سطر | ViewModel + UseCase + Repository |
| OCP | switch-case حسب نوع الدفع | Strategy: واجهة PaymentGateway |
| LSP | خلل عند استبدال BaseViewModel | التحقق من عقد الفئات الفرعية |
| ISP | UnsupportedOperationException | فصل Reader / Writer |
| DIP | ViewModel ينشئ Retrofit يدويًا | حاوية DI Hilt / Koin |
أخطاء SOLID غالبًا ما ترتبط بتعقيد الكود المفرط. الأول — اتباع المبادئ حرفيًا دون مراعاة السياق. تقسيم فصل UserService واحد إلى 10 واجهات و15 فصلًا فقط من أجل ISP «نظيف» هو هندسة مفرطة. SOLID أداة وليس هدفًا. الخطأ الثاني — الخلط بين SRP و«طريقة واحدة = مسؤولية واحدة». يمكن أن يكون للفصل عدة طرق إذا كانت جميعها تنتمي إلى نفس مجال المسؤولية.
وفقًا لـ Simple Thread، 2024، الخطأ الثالث — تجاهل LSP عند وراثة ViewModel في Android. إذا كانت ViewModel الأساسية تتوقع LiveData ولكن التابعة تستخدم StateFlow — كود العميل المشترك على LiveData لن يتلقى التحديثات. الرابع — انتهاك DIP من أجل الاختبار: RepositoryImpl ينشئ مباشرة مثيل OkHttpClient، مما يجعل الاختبارات الوحدوية مستحيلة.
قاعدة الوسط الذهبي: طبق SOLID عندما يحل مشكلة حقيقية (تغييرات متكررة، صعوبة اختبار، تكرار). لشاشات CRUD البسيطة، الالتزام الصارم بجميع المبادئ الخمسة مفرط. لمنطق الأعمال والحسابات المالية والتفاعلات مع API، SOLID ضروري.
Clean Architecture (Robert C. Martin، 2012) — تطبيق مباشر لـ SOLID على مستوى طبقات التطبيق. SRP يحدد حدود حالات الاستخدام (كل حالة استخدام — فصل واحد). OCP يُطبق من خلال واجهات المستودع (طبقة البيانات يمكن أن تتغير دون تعديل Domain). ISP يوفر فصل حالة الاستخدام إلى حدود إدخال/إخراج. DIP — اتجاه التبعيات نحو داخل طبقة Domain. LSP يضمن أن أي تنفيذ للمستودع قابل للاستبدال دون كسر حالات الاستخدام.
الأسئلة الشائعة
SOLID — خمس قواعد لكتابة كود سهل التغيير والاختبار والفهم. كل حرف هو مبدأ: لا تكتب فصولاً كبيرة (SRP)، لا تغير كودًا موجودًا — أضف جديدًا (OCP)، لا تكسر سلوك الفئات الفرعية (LSP) وغيرها.
SRP (Single Responsibility) يعتبر الأهم لأن انتهاكه يؤدي إلى God classes — فصول ضخمة يصعب اختبارها وتغييرها. ومع ذلك، بدون DIP (Dependency Inversion) يبقى الكود مقترنًا بشدة، وهو أمر بالغ الأهمية أيضًا.
ليس إلزاميًا، لكنه مرغوب بشدة للمشاريع التجارية ذات دورة الحياة الطويلة. للتطبيقات البسيطة (شاشة واحدة، لا منطق أعمال)، قد يكون SOLID مفرطًا. للمشاريع ذات 50+ شاشة و3+ مطورين، SOLID هو الحد الأدنى الضروري.
النتائج: تصبح الفصول «كبيرة» (1000+ سطر)، تغيير في مكان واحد يكسر ثلاثة أماكن أخرى، يستحيل كتابة اختبارات وحدوية، إضافة ميزة جديدة تستغرق أسابيع بدلاً من أيام. بمرور الوقت، يتحول الكود إلى «كرة طين كبيرة» — مشوش وهش.
علامات الامتثال: كل فصل أقل من 200 سطر، تغيير ميزة لا يؤثر على 5+ ملفات، تُكتب الاختبارات دون محاكاة 10 تبعيات، مطور جديد يفهم الهيكل في يوم واحد. أدوات مثل SonarQube وdetekt تساعد في اكتشاف انتهاكات SRP وDIP.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.