مبادئ SOLID في Kotlin: نظرية وتطبيق على المستوى المتقدم
\n\nSOLID هي مجموعة أساسية من مبادئ التصميم الشيئي، قدمها روبرت مارتن. في سياق Kotlin، الذي يجمع بين أفضل ميزات البرمجة الشيئية والبرمجة الوظيفية، تكتسب هذه المبادئ إمكانيات جديدة للتنفيذ. في هذه المقالة، سننظر في كل مبدأ على المستوى المتقدم، باستخدام ميزات فريدة للغة: الدوال المضمنة، التفويض، الفئات المختومة ودوال الامتداد.
\n\n1. مبدأ المسؤولية الواحدة (SRP) والتفويض
\n\nينص مبدأ المسؤولية الواحدة على: يجب أن يكون للفصل سبب واحد فقط للتغيير. في Kotlin، للامتثال لـ SRP، غالبًا ما يُستخدم نمط التفويض (Delegation). الكلمة المفتاحية by تسمح بتفويض تنفيذ الواجهة تلقائيًا إلى كائن آخر.
المشكلة: الفصل UserService مسؤول عن منطق الأعمال والتسجيل وإرسال البريد الإلكتروني.
// ❌ انتهاك SRP\nclass UserService {\n fun registerUser(email: String, password: String) {\n // منطق الأعمال\n println("User registered")\n // التسجيل\n log("User registered: $email")\n // إرسال البريد الإلكتروني\n sendEmail(email, "Welcome!")\n }\n\n private fun log(message: String) { /* ... */ }\n private fun sendEmail(to: String, body: String) { /* ... */ }\n}\n\n\nالحل باستخدام التفويض:
\n\n// ✅ الامتثال لـ SRP عبر التفويض\ninterface Logger {\n fun log(message: String)\n}\n\ninterface EmailSender {\n fun send(to: String, body: String)\n}\n\nclass ConsoleLogger : Logger {\n override fun log(message: String) = println("LOG: $message")\n}\n\nclass SmtpEmailSender : EmailSender {\n override fun send(to: String, body: String) = println("Sending email to $to: $body")\n}\n\nclass UserService(\n private val logger: Logger,\n private val emailSender: EmailSender\n) : Logger by logger, EmailSender by emailSender { // 👈 تفويض\n\n fun registerUser(email: String, password: String) {\n println("User registered")\n log("User registered: $email")\n send(email, "Welcome!")\n }\n}\n\n// الاستخدام\nfun main() {\n val service = UserService(ConsoleLogger(), SmtpEmailSender())\n service.registerUser("user@example.com", "pass123")\n}\n\n\nاستخدام by يتيح تجنب الكود النمطي والفصل الواضح للمسؤوليات. الفصل UserService الآن مسؤول فقط عن التسجيل، بينما تم تفويض التسجيل والإرسال إلى فصول منفصلة.
2. مبدأ مفتوح-مغلق (OCP) والفئات المختومة
\n\nمبدأ مفتوح-مغلق: يجب أن تكون الفصول مفتوحة للتمديد ولكن مغلقة للتعديل. في Kotlin، الفئات المختومة ودوال الامتداد مثالية لهذا الغرض.
\n\nالمشكلة: الدالة processPayment تستخدم when مع التحقق من النوع، وعند إضافة طريقة دفع جديدة، يجب تعديلها.
// ❌ انتهاك OCP\nfun processPayment(type: String, amount: Double) {\n when (type) {\n "card" -> println("Processing card payment: $amount")\n "paypal" -> println("Processing PayPal payment: $amount")\n else -> throw IllegalArgumentException("Unknown payment type")\n }\n}\n\n\nالحل باستخدام الفئات المختومة:
\n\n// ✅ الامتثال لـ OCP\nsealed class PaymentMethod {\n data class Card(val number: String, val cvv: String) : PaymentMethod()\n data class PayPal(val email: String) : PaymentMethod()\n object Cash : PaymentMethod()\n}\n\n// الدالة مغلقة للتعديل\nfun processPayment(method: PaymentMethod, amount: Double) {\n // عند ظهور فئات فرعية جديدة، سيطلب المترجم معالجتها\n