DRY (Don't Repeat Yourself) هو مبدأ أساسي في التطوير صاغه آندي هانت وديف توماس في كتاب «The Pragmatic Programmer.» ينص على أن كل جزء من المعرفة في النظام يجب أن يكون له تمثيل واحد لا لبس فيه وموثوق. وفقًا لـ The Pragmatic Programmer, 20th Anniversary Edition، فإن انتهاك DRY يؤدي إلى أن تغيير عنصر واحد يتطلب تعديلات في عشرات الأماكن، وكل جزء مفقود يصبح مصدرًا للأخطاء.
أهم النقاط
DRY (Don't Repeat Yourself) هو مبدأ تطوير يتطلب تخزين كل عنصر معرفة في المشروع مرة واحدة بالضبط. هذا يعني أن أي منطق أو تكوين أو بيانات وصفية يجب أن توجد في مكان واحد فقط.
تم تقديم المصطلح من قبل آندي هانت وديف توماس في عام 1999 في كتاب «The Pragmatic Programmer.» عرّف المؤلفان DRY بأنه «يجب أن يكون لكل جزء من المعرفة تمثيل واحد لا لبس فيه وموثوق داخل النظام.» عكس DRY هو نهج WET (Write Everything Twice)، حيث يعتبر التكرار أمرًا طبيعيًا.
وفقًا لدراسة University of California, Davis (2019)، فإن المشاريع ذات المستويات العالية من تكرار الكود تنفق وقتًا أكثر بنسبة 42% على إصلاح الأخطاء. السبب هو أن المطورين يجب أن يجدوا ويغيروا جميع نسخ نفس الجزء — والبحث اليدوي يؤدي حتمًا إلى إغفالات.
طبق DRY كمعيار لجودة الكود. إذا لاحظت أن نفس النمط يظهر ثلاث مرات في المشروع — استخرجه في تجريد دون انتظار التكرار الرابع.
مبدأ المسؤولية الواحدة (SRP) من SOLID ينص على أن الفئة يجب أن يكون لها سبب واحد للتغيير. DRY أوسع: فهو لا يغطي الفئات فقط بل أيضًا البيانات والتكوين والوثائق وحتى قواعد العمل. SRP يتعلق بحدود المسؤولية، بينما DRY يتعلق بمنع النسخ.
في التطوير المحمول، هذا الاختلاف ملحوظ بشكل خاص. إذا تكررت نفس قاعدة العمل (حساب الضريبة، تنسيق التاريخ) في جزئي Android و iOS من المشروع — فهذا انتهاك لـ DRY، على الرغم من أن SRP مطبق رسميًا داخل كل منصة. الحل هو استخراج المنطق المشترك في وحدة مشتركة (KMM, C++).
وفقًا لتقرير Google Android Architecture Guidelines (2023)، فإن الفرق التي تستخدم وحدات مشتركة للمنطق التجاري تقلل عدد الأخطاء عند تغيير المتطلبات بنسبة 37% مقارنة بالمشاريع التي تكرر المنطق عبر المنصات.
التكرار هو المصدر الرئيسي للديون التقنية في المشاريع المحمولة. كل نسخة من الكود تخلق اعتمادًا خفيًا: لتغيير السلوك، يجب العثور على جميع النسخ وتحديثها. تفويت أي نسخة يعني خطأ.
تأمل سيناريو كلاسيكيًا: في تطبيق Android، يتم تنسيق التاريخ في ثلاثة أنشطة مختلفة. عند الانتقال إلى تنسيق جديد (مثل ISO 8601)، يصلح المطور ملفين وينسى الثالث — ويرى المستخدم التواريخ بالتنسيق القديم. ينخفض تقييم التطبيق ويستغرق العثور على الخطأ ضعف الوقت.
أظهرت دراسة Google Research (2020) أن 68% من الأخطاء الحرجة في التطبيقات المحمولة مرتبطة بتغييرات غير متزامنة في الكود المكرر. علاوة على ذلك، فإن إصلاح مثل هذا الخطأ في الإنتاج يكلف 4.5 أضعاف ما لو كان الكود موحدًا من البداية.
استخدم محللات ثابتة (Detekt, SwiftLint) مع قواعد تكتشف copy-paste. قم بتكوين CI بحيث لا تمر طلبات السحب التي تحتوي على أكثر من N سطر من التكرار عبر المراجعة بدون مبرر.
النمط المعاكس النموذجي هو نسخ محول RecyclerView مع تعديلات طفيفة. بدلاً من محول واحد شامل مع تكوين، ينشئ المطورون فئة منفصلة لكل شاشة. إعادة الهيكلة عن طريق استخراج فئة أساسية مشتركة تقلل الكود بنسبة 30–50%.
// التكرار: محولان منفصلان
class UserAdapter {
fun bind(item: User) { /* ... */ }
}
class ProductAdapter {
fun bind(item: Product) { /* ... */ }
}
// إعادة هيكلة DRY: فئة أساسية مشتركة
abstract class BaseAdapter<T> {
abstract fun bind(item: T)
}
class UserAdapter : BaseAdapter<User>() { /* ... */ }
class ProductAdapter : BaseAdapter<Product>() { /* ... */ }
في المثال الأول، يعيد كل محول تنفيذ آلية الربط من الصفر. عند إضافة منطق جديد (تحليلات، تسجيل)، سيتعين تغيير كل ملف. فئة أساسية تلغي هذا التكرار: المنطق المشترك يعيش في مكان واحد، والمنطق المحدد في الفئات الفرعية.
في مشاريع iOS، غالبًا ما يتم تكرار تكوين URLSession — الرؤوس، المهلات، معالجة الأخطاء. كل خدمة تنشئ جلستها الخاصة بإعدادات متكررة.
// التكرار: كل خدمة تعد الجلسة من جديد
class UserService {
let session = URLSession(configuration: {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return cfg
}())
}
// DRY: مصنع جلسات موحد
struct NetworkConfig {
static var session: URLSession {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return URLSession(configuration: cfg)
}
}
استخراج التكوين في NetworkConfig موحد يضمن أن جميع الخدمات تستخدم نفس الرؤوس والمهلات. تغيير في مكان واحد ينطبق تلقائيًا على جميع الطلبات — مما يقلل من خطر الأخطاء عند تغيير مفاتيح API أو إصدارات البروتوكول.
الوراثة هي طريقة طبيعية لإزالة التكرار: يتم نقل المنطق المشترك إلى فئة أساسية والمنطق المحدد إلى فئات فرعية. ومع ذلك، في التطوير المحمول، يؤدي الإفراط في استخدام الوراثة إلى إنشاء تسلسلات هرمية جامدة يصعب صيانتها. التكوين (حقن التبعية) هو بديل أكثر مرونة.
أظهر تحليل من Google I/O 2023: Modern Android Architecture أن 76% من فرق Google تفضل التكوين على الوراثة لإزالة التكرار. بدلاً من BaseViewModel بعشرات الطرق، يوصى باستخراج فئات UseCase منفصلة لكل عملية تجارية وحقنها حيثما تكون مطلوبة.
اختر التكوين في جميع الحالات باستثناء علاقات «هو». إذا كانت الفئة A تخصصًا للفئة B — فالوراثة مناسبة. إذا كانت A تستخدم ببساطة وظائف B — استخدم التكوين.
الفئات المساعدة (Extensions, Helpers) هي أبسط طريقة لتجنب التكرار. المرشحون النموذجيون: تنسيق التواريخ، التحقق من البريد الإلكتروني، تحويل الوحدات، العمل مع SharedPreferences/UserDefaults.
// DRY: دالة موحدة لتنسيق التاريخ
fun Date.toDisplayFormat(): String {
val sdf = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
return sdf.format(this)
}
// الاستخدام في أي مكان في التطبيق
textView.text = Date().toDisplayFormat()
الإضافة Date.toDisplayFormat() تُعلن مرة واحدة وتكون متاحة في جميع أنحاء المشروع. إذا احتاج التنسيق إلى التغيير من «dd.MM.yyyy» إلى «yyyy-MM-dd» — التصحيح في ملف واحد، وليس في كل Activity أو Fragment حيث يحدث التنسيق. هذا هو جوهر DRY.
غالبًا ما تكرر مشاريع Android متعددة الوحدات إصدارات التبعيات في كل build.gradle. الحل هو فهرس الإصدارات (libs.versions.toml) الذي يركز جميع الإصدارات في ملف واحد.
وفقًا لـ وثائق مطوري Android (2024)، فإن الترحيل إلى فهرس الإصدارات يقلل تعارضات التبعيات بنسبة 52% ويسرع البناء من خلال نقطة تحرير واحدة.
طبق فهرس الإصدارات في بداية المشروع أو أثناء إعادة التنظيم الأولى للوحدات. إذا كان المشروع يحتوي بالفعل على تكرار — خصص يومًا واحدًا للترحيل: سيعوض نفسه في تحديث المكتبة التالي.
التجريد المبكر هو أكثر خطأ شائع بين المبتدئين. يرى المطور سطرين متشابهين من الكود ويستخرجهما فورًا في دالة مشتركة. بعد شهر، تتغير المتطلبات وتصبح الدالة المشتركة مليئة بالمعلمات والأعلام — أكثر تعقيدًا من التكرار الأصلي. قاعدة الثلاثة تحمي تمامًا من هذا: لا تجرد شيئًا ظهر مرة أو مرتين فقط.
مارتن فاولر في كتابه Refactoring (2019) يوصي: «تكرار الكود ليس دائمًا شرًا. تكرار المعرفة هو الشر.» إذا تطابقت سطران بالصدفة ولكن يعبران عن مفاهيم مختلفة — فهذا ليس تكرارًا، إنها مصادفة. قاعدة الثلاثة تساعد في التمييز بين المصادفة العرضية والتكرار المنهجي.
قبل التجريد، قيم الدلالات. الكود المنسوخ بنفس المعنى — انتهاك لـ DRY. كود بمعنى مختلف ولكن بناء جملة مشابه — مصادفة لا تتطلب تجريدًا.
الإفراط في الوسم يحدث عندما تحاول دالة واحدة تغطية جميع السيناريوهات الممكنة من خلال الأعلام والمعلمات المنطقية. هذا الكود ينتهك SRP ويصبح غير قابل للقراءة. عرض: إذا كانت الدالة تحتوي على أكثر من معلمتين منطقيتين — فهذه رائحة كود تدل على الإفراط في التجريد.
بدلاً من دالة واحدة بعلم useCache: Boolean، من الأفضل إنشاء دالتين منفصلتين بأسماء واضحة: fetchFromNetwork() و fetchFromCache(). الوضوح أكثر أهمية من التجريد الجاف — وهذا يتوافق مع مبدأ KISS.
أعد هيكلة الإفراط في الوسم عندما تصل الدالة إلى 3+ معلمات منطقية. قسم إلى دوال منفصلة بأسماء واضحة — سيصبح كل استدعاء موثقًا ذاتيًا.
الأسئلة الشائعة
DRY (Don't Repeat Yourself) هو مبدأ يتطلب تخزين كل وحدة منطقية في مكان واحد. إذا ظهر نفس الكود في أجزاء متعددة من المشروع — فهذا انتهاك لـ DRY. الحل: استخرج المنطق المتكرر في دالة أو فئة أو وحدة منفصلة.
WET (Write Everything Twice) هو عكس DRY، حيث يعتبر التكرار مقبولاً. في مشاريع WET، يمكن أن يوجد نفس الجزء من الكود في خمس نسخ، وعندما تتغير المتطلبات، يصلح المطور كل نسخة على حدة. WET يزيد من خطر الأخطاء ويبطئ التطوير.
DRY يضر عندما يؤدي إلى تجريد مبكر: عندما يتم دمج قسمين متشابهين ولكن مختلفين دلاليًا من الكود قسرًا في دالة واحدة. هذا يولد كودًا معقدًا ومثقلًا بالمعلمات. قاعدة الثلاثة تساعد في تجنب هذا الخطأ: جرد فقط بعد التكرار الثالث.
في Android، يتم تطبيق DRY من خلال فهارس الإصدارات (libs.versions.toml)، والفئات الأساسية المشتركة للمحولات، ومصانع ViewModel، وإضافات Kotlin المساعدة. يوصى باستخراج منطق الأعمال في وحدات مشتركة (KMM) واستخدام View Binding لإزالة تكرار findViewById.
في iOS، يتم تحقيق DRY من خلال البروتوكولات مع التنفيذ الافتراضي، والتكوينات الشبكية المشتركة (NetworkConfig)، ومصانع خلايا UICollectionView، وحزم SPM مع منطق الأعمال المشترك. الإضافات للأنواع القياسية (Date, String, URL) تقلل التكرار في التنسيق والتحقق.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا