Code Smell هو مؤشر سطحي في الكود يشير إلى مشكلة محتملة في تصميم أو بنية التطبيق. صاغ المصطلح كينت بيك وروج له مارتن فاولر في كتاب «Refactoring: Improving the Design of Existing Code». وفقاً لـ Martin Fowler، رائحة الكود لا تعني بالضرورة خطأ، لكنها تشير دائماً تقريباً إلى الحاجة لإعادة الهيكلة لتحسين قابلية الصيانة.
النقاط الرئيسية
Code Smell هو استعارة للدلالة على أعراض في الكود المصدري تشير باحتمالية عالية إلى مشاكل أعمق. المصطلح نفسه ليس له تعريف رسمي — إنه استرشاد مبني على خبرة المطورين. قام مارتن فاولر وكينت بيك أول مرة بتنظيم 22 رائحة في عام 1999 في كتاب «Refactoring»، ومعظمها لا يزال ذا صلة بعد عقود.
من المهم فهم الفرق بين Code Smell والخطأ. الرائحة ليست خطأ: الكود يترجم ويعمل وينتج نتائج صحيحة. المشكلة أن مثل هذا الكود يصعب قراءته وتعديله واختباره. مع الوقت، تزداد تكلفة كل تغيير وتقل الثقة في صحة إعادة الهيكلة. أدوات التحليل الثابت (SonarQube، Detekt، SwiftLint) تكتشف تلقائياً العديد من الروائح.
الطبيعة الاسترشادية لـ Code Smell تعني أنه ليس كل طريقة طويلة يجب تقسيمها، وليس كل صنف كبير يحتاج إعادة هيكلة. القرار يتخذه المطور بتقييم السياق: تكرار التغييرات، أهمية الوحدة، خطط التطوير. المهندسون ذوو الخبرة يشعرون بالرائحة حدسياً — الكود «رائحته كريهة» رغم أن جميع القواعد الرسمية مطبقة.
حدد فاولر 22 رائحة مقسمة إلى عدة فئات. للتطوير المحمول، الأكثر أهمية هي الروائح الهيكلية، وروائح التصميم كائني التوجه، والمشاكل المحددة المتعلقة بقيود المنصة. دعنا نفحص كل مجموعة بأمثلة من الواقع العملي.
Long Method هي الرائحة الأكثر شيوعاً في التطبيقات المحمولة. شاشة نموذج التسجيل غالباً تحتوي على طريقة setupUI واحدة بطول 200+ سطر تنشئ كل الـ Views، وتضبط القيود، وتشترك في الأحداث، وتعالج الأخطاء. الحل: التقسيم إلى طرق حسب الكتل المنطقية — configureEmailField، configurePasswordField، setupConstraints، bindViewModel.
Large Class — Activity أو ViewController مسؤولة عن العرض والتنقل ومنطق الأعمال والتفاعلات الشبكية كلها معاً. هذا الصنف ينتهك مبدأ المسؤولية الواحدة ويحتوي على عشرات الحقول والطرق. في Android، غالباً ما يكون Fragment بـ 1000+ سطر يحتوي على منطق شاشات مختلفة. الحل: استخراج presenter/ViewModel، ونقل كود الشبكة إلى repository، والتنقل إلى coordinator.
Duplicate Code — نسخ كتل متطابقة في أجزاء مختلفة من التطبيق. مثال نموذجي: شاشتان تعرضان بطاقة منتج — في الكتالوج وفي المفضلة. إذا تم نسخ منطق العرض، فإن إصلاح خطأ في مكان واحد لن يصلحه في الآخر. الحل: استخراج المنطق المشترك إلى مكون أو امتداد قابل لإعادة الاستخدام.
Feature Envy — طريقة من صنف تستخدم بكثافة بيانات صنف آخر. في Android، يظهر هذا عندما تصل ViewModel مباشرة إلى حقول نموذج User بدلاً من استدعاء طريقة النموذج. الإشارة: إذا كان يمكن نقل طريقة إلى الصنف الذي تستخدم بياناته — انقلها. Switch Statements (سلاسل الشروط) — بناء switch أو سلسلة if-else تتحقق من نوع الكائن. بدلاً من ذلك، استخدم تعدد الأشكال أو نمط الاستراتيجية.
Data Class — صنف يخزن البيانات فقط لكنه لا يحتوي على سلوك. data classes في Kotlin أو الهياكل في Swift ليست رائحة بحد ذاتها. المشكلة تنشأ عندما يكون منطق الأعمال الذي يعمل مع هذه البيانات مبعثراً في قاعدة الكود بدلاً من أن يكون مغلفاً. Refused Bequest — صنف فرعي لا يستخدم معظم طرق الصنف الأب ويتجاوزها بأكواد وهمية فارغة. علامة على وراثة غير صحيحة: استبدل الوراثة بالتركيب.
God Activity / God Fragment — Activity أو Fragment تعرف كل شيء: دورة الحياة، البيانات، التنقل، الأذونات، حقن التبعيات. هذا هو أغلى صنف في الصيانة في التطبيق. الحل: أنماط معمارية MVVM أو MVI أو Clean Architecture تفصل المسؤوليات. Giant ViewController — المكافئ في iOS، حيث UIViewController يحتوي على كل منطق الشاشة ويتجاوز غالباً 500 سطر.
Hardcoded Resources — نصوص وألوان وأحجام وعناوين API مضمنة مباشرة في الكود. في Android، هذا ينتهك نظام الموارد R؛ في iOS، NSLocalizedString و Asset Catalog. الإصلاح: نقل كل النصوص إلى strings.xml أو Localizable.strings، والعناوين إلى ملف إعدادات، والأحجام إلى dimens. Leaking Context — الاحتفاظ بمرجع إلى Activity أو ViewController لفترة أطول من عمر المكون نفسه. يؤدي إلى تسرب الذاكرة وتعطل التطبيق. الحل: مراجع ضعيفة، Jetpack Lifecycle، RxSwift DisposeBag.
| الرائحة | أين تظهر | الحل |
|---|---|---|
| Long Method | Android/iOS | Extract Method، التقسيم |
| Large Class | Activity، ViewController | MVVM، VIPER، Clean Arch |
| Duplicate Code | أي شاشة | مكون مشترك، DRY |
| Feature Envy | ViewModel، Presenter | Move Method |
| Leaking Context | Android | مكونات Lifecycle-aware |
مراجعة الكود هي الطريقة الأكثر موثوقية لاكتشاف الروائح. العين البشرية تلاحظ التركيبات غير الطبيعية التي تفوتها المحللات الآلية. تتحسن فعالية مراجعة الكود عندما يستخدم الفريق قائمة تحقق من الروائح النموذجية. يُنصح بمراجعة ما لا يزيد عن 200–400 سطر كود في الجلسة الواحدة — بعد هذا الحد ينخفض الانتباه وتبدأ الروائح في التسلل.
التحليل الثابت يؤتمت البحث عن الروائح الهيكلية. لنظام Android، الأدوات القياسية هي Detekt (Kotlin) و Android Lint؛ لنظام iOS، SwiftLint و SonarQube. تجد هذه الأدوات الطرق الطويلة والأصناف الكبيرة والكود المكرر والعديد من المشاكل الأخرى. من المهم ضبط القواعد حسب المشروع — الإعدادات الافتراضية غالباً ما تكون صارمة جداً أو على العكس تغفل روائح حرجة.
مقاييس الكود توفر معايير موضوعية: التعقيد الدوري (الحد >10 يتطلب انتباهاً)، أسطر الكود لكل طريقة (الحد >30)، عمق الوراثة (>3 — سبب للتفكير). أدوات مثل CodeMetrics (Xcode) و Gradle Metrics Plugin تبني رسوماً بيانية لتغير المقاييس مع الوقت. إذا نما تعقيد طريقة من 5 إلى 15 بعد آخر commit — فهذه إشارة لإعادة الهيكلة.
// مثال: طريقة بتعقيد دوري = 7 (فوق الحد 5)
fun processOrder(order: Order) {
if (order.status == Status.NEW) { /* 10 أسطر */ }
else if (order.status == Status.PAID) { /* 15 سطراً */ }
else if (order.status == Status.SHIPPED) { /* 20 سطراً */ }
else if (order.status == Status.DELIVERED) { /* 8 أسطر */ }
else if (order.status == Status.CANCELLED) { /* 5 أسطر */ }
else { throw IllegalStateException() }
}
// الحل: تعدد الأشكال بدلاً من switch
interface OrderHandler {
fun handle(order: Order)
}
الكشف الآلي عن الروائح لا يغني عن مراجعة الكود: المحللات الثابتة تجد فقط المشاكل الهيكلية لكنها لا تلتقط الروائح الدلالية (Feature Envy، Inappropriate Intimacy). مزيج الأدوات الآلية والمراجعة البشرية يعطي أفضل النتائج. هيئ خط أنابيب CI/CD بحيث يفشل البناء عند تجاوز حدود التعقيد أو طول الطريقة.
إعادة الهيكلة هي الطريقة الأساسية للتخلص من روائح الكود. يصف فاولر عشرات تقنيات إعادة الهيكلة، كل منها ينطبق على رائحة معينة. Extract Method — للطرق الطويلة، Extract Class — للأصناف الكبيرة، Move Method — لـ Feature Envy. من المهم تنفيذ إعادة الهيكلة بخطوات صغيرة مع الحفاظ على عمل الكود بعد كل تغيير.
الاختبارات قبل إعادة الهيكلة شرط إلزامي. إذا لم يكن الكود مغطى باختبارات الوحدة، تتحول إعادة الهيكلة إلى إعادة كتابة بنتيجة غير معروفة. للكود القديم بدون اختبارات، استخدم اختبارات التوصيف — اكتب اختبارات تثبت السلوك الحالي، ثم أعد الهيكلة. الاختبارات تعطي الثقة بأن منطق الأعمال لم ينكسر بعد إعادة الهيكلة.
التدرج هو مفتاح النجاح في إصلاح الروائح في التطوير المحمول. لا تحاول إعادة كتابة God Activity بالكامل. استخرج أولاً طبقة التنقل، ثم طبقة البيانات، ثم منطق العرض. كل خطوة يصاحبها commit وتشغيل اختبارات. استخدم مفاتيح الميزات لتفعيل إعادة الهيكلة لجزء من المستخدمين والتراجع عند ظهور مشاكل.
أدوات بيئة التطوير تؤتمت العديد من تقنيات إعادة الهيكلة. Android Studio و IntelliJ IDEA تقدم إعادة هيكلة مدمجة: Extract Method، Extract Interface، Pull Members Up، Encapsulate Fields. Xcode (ابتداءً من الإصدار 14) حسّن دعم إعادة الهيكلة لـ Swift. استخدام إعادة الهيكلة الآلية يقلل من خطر الأخطاء مقارنة بنسخ الكود يدوياً.
التطوير المحمول يضيف روائحه الخاصة المرتبطة بقيود المنصة. في Android، تشمل تسرب Context، وعدم إغلاق Cursor، واستخدام غير صحيح لدورة الحياة. في iOS، دورات الاحتفاظ عبر closures، والتعامل غير الصحيح مع Auto Layout، و ViewControllers العملاقة. هذه الروائح لا تؤثر فقط على قابلية الصيانة بل تؤثر مباشرة على أداء وثبات التطبيق.
Callback Hell — رائحة مميزة للكود الذي يعمل مع العمليات غير المتزامنة. الاستدعاءات المتداخلة تجعل الكود غير قابل للقراءة وصعب التصحيح. الحل: coroutines (Kotlin)، async/await (Swift 5.5+)، RxJava/RxSwift أو Combine. وفقاً لـ Google I/O 2023، المشاريع التي انتقلت من نمط الاستدعاءات إلى coroutines خفضت عدد الأخطاء بنسبة 30% وسرعت إضافة الميزات الجديدة.
Platform Coupling — ارتباط وثيق لمنطق الأعمال بمكونات المنصة. اختبار مثل هذا المنطق يتطلب تشغيل محاكي مما يبطئ دورة التغذية الراجعة. الحل: Clean Architecture تفصل الكود إلى طبقات Domain (Kotlin/Swift نقي بدون تبعيات منصة) و Data/UI (مع تبعيات منصة). يتم اختبار منطق الأعمال على JVM بدون محاكي.
الأسئلة الشائعة
لا — Code Smell ليس خطأ. الكود ذو الرائحة يعمل بشكل صحيح، لكن من الصعب صيانته وتعديله واختباره. الخطأ هو سلوك غير صحيح؛ الرائحة هي تحذير من مشاكل محتملة في المستقبل.
22 رائحة في الطبعة الثانية من كتاب «Refactoring» (2019). من بينها Long Method، Large Class، Primitive Obsession، Data Clumps، Switch Statements، Speculative Generality وغيرها. أضاف المجتمع عشرات الروائح الجديدة للنماذج والمنصات الحديثة.
مزيج يعطي أفضل نتيجة: Detekt (Android/Kotlin)، SwiftLint (iOS)، SonarQube (كلاهما) للتحليل الآلي ومراجعة الكود للروائح الدلالية. لا توجد أداة واحدة تجد 100% من المشاكل — الخبرة البشرية تبقى حاسمة.
نعم، إذا كان الكود يتغير نادراً أو ستتم إعادة كتابته بالكامل قريباً. لكن تراكم الروائح يتحول إلى دين تقني: كل تغيير جديد يصبح أصعب، وتكلفة الإصلاح تنمو بشكل هائل.
نعم — الأطر التصريحية ولدت روائح جديدة: كتل @State عملاقة، معالجة غير صحيحة لإعادة التصيير، إعادة تركيب مفرطة، وعدم استخراج إلى Views منفصلة. لـ SwiftUI، الرائحة النموذجية هي Massive View بعشرات متغيرات @State.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا