مقدمة: لماذا الكود القديم (Legacy) ليس حكماً بالإعدام
\nكل مطور واجه كوداً قديماً (Legacy) - ذلك "الصندوق الأسود" الذي تخشى لمسه لأن "هو شغال، ومش لازم نتدخل". لكن، عندما تبدأ قاعدة الكود بلغة Kotlin (أو Java التي يتم ترحيلها تدريجياً) في إبطاء التطوير، وتراكم الأخطاء، وإخافة أعضاء الفريق الجدد، يحين وقت إعادة الهيكلة. إعادة هيكلة الكود القديم (Legacy) بلغة Kotlin ليست مجرد إعادة كتابة، بل هي عملية منهجية لتحسين البنية الداخلية دون تغيير السلوك الخارجي.
\nفي هذه المقالة، سنستعرض المسار الكامل: من تحليل الحالة الحالية إلى تطبيق الممارسات الحديثة في Kotlin. ستتعلم كيفية تحويل الكود القديم بأمان باستخدام إمكانيات اللغة (العدميّة (Nullability)، دوال الإضافة (Extension Functions)، الفئات المختومة (Sealed Classes)) دون كسر بيئة الإنتاج (Production).
\n\n1. التشخيص: كيفية تقييم حجم الكود القديم (Legacy)
\nقبل البدء في إعادة الهيكلة، يجب أن تفهم ما تتعامل معه. غالباً ما يأتي الكود القديم (Legacy) بلغة Kotlin من مشاريع بدأت بالكتابة بلغة Java ثم تم ترحيلها جزئياً. العلامات النموذجية:
\n- \n
- الكائنات العملاقة (God Objects) - فئات تفعل كل شيء: من التحليل (Parsing) إلى منطق الأعمال (Business Logic). \n
- غياب الاختبارات - الكود غير مغطى باختبارات الوحدة (Unit Tests)، وأي تغيير محفوف بالمخاطر. \n
- البيانات القابلة للتغيير (Mutable Data) - وفرة من
varبدلاً منval، وقوائم قابلة للتغيير (Mutable Lists) يتم تمريرها بين الوحدات. \n - كابوس العدم (Null Nightmare) - عامل
!!منتشر في جميع أنحاء الكود، وتحققّات لا نهائيةif (x != null). \n
للتشخيص، استخدم الأدوات: Detekt (التحليل الثابت لـ Kotlin)، SonarQube (مقاييس الجودة)، و IntelliJ IDEA Inspections. أنشئ قائمة مراجعة للنقاط الإشكالية وحدد أولوياتها حسب المخاطر وتكرار الاستخدام.
\n\n1.1. رسم خريطة التبعيات
\nخذ ورقة أو استخدم PlantUML لتصور العلاقات بين الفئات. إذا كان رسم التبعيات يشبه شبكة العنكبوت - فهذا جرس إنذار. انتبه بشكل خاص للتبعيات الدائرية: فهي تجعل الكود هشاً وصعب الاختبار.
\n\n2. استراتيجية إعادة الهيكلة: خطة عمل خطوة بخطوة
\nإعادة الهيكلة بدون خطة هي طريق نحو الكارثة. استخدم نهج "تقنية الخطوات الصغيرة" (Baby Steps). خوارزمية العمل:
\n- \n
- وثّق السلوك - اكتب اختبارات التوصيف (Characterization Tests) التي تثبت السلوك الحالي للنظام. \n
- اعزل التغييرات - اعمل في فرع منفصل، واستخدم مفاتيح الميزات (Feature Toggles) للأجزاء المعقدة. \n
- إعادة الهيكلة وفق الأنماط - طبق تقنيات مجربة (Extract Method، Replace Conditional with Polymorphism، Introduce Parameter Object). \n
- الترحيل التدريجي - لا تحاول إعادة كتابة كل شيء دفعة واحدة. استخدم تعليقات
@Deprecatedللطرق القديمة. \n
2.1. مثال: استبدال if-else بـ when والفئات المختومة (Sealed Classes)
\nالكود القديم (Legacy) النموذجي بلغة Kotlin يحتوي على سلاسل if-else لمعالجة الحالات:
\n// سيء: علامة قابلة للتغيير و if-else\nvar status = "active"\nif (status == "active") {\n // ...\n} else if (status == "inactive") {\n // ...\n} else {\n throw Exception("Unknown status")\n}\nإعادة الهيكلة باستخدام الفئات المختومة (Sealed Classes):
\n// جيد: نموذج آمن من حيث النوع\nsealed class Status {\n object Active : Status()\n object Inactive : Status()\n data class Custom(val reason: String) : Status()\n}\n\nfun handleStatus(status: Status) = when (status) {\n is Status.Active -> // معالجة\n is Status.Inactive -> // معالجة\n is Status.Custom -> // معالجة مع reason\n}\nهذا يلغي الحاجة إلى else ويجعل الكود قابلاً للتوسع.
3. العمل الآمن مع القيم القابلة للعدم (Nullable) والمجموعات (Collections)
\nالكود القديم (Legacy) غالباً