SRP: ما هو، مبدأ المسؤولية الواحدة في التطوير

المؤلف: IT Sectr نُشر: 2026-05-11 وقت القراءة: 9 دق

SRP (Single Responsibility Principle) — أول مبدأ من مبادئ SOLID، وهو ينص على: يجب أن يكون لكل فئة أو وحدة سبب واحد فقط للتغيير. صاغ هذا المبدأ روبرت مارتين في كتاب Clean Architecture (2017) وأصبح أساس التصميم الوحداتي. وفقًا لهذا الكتاب، تطبيق SRP يقلل مباشرة اقتران المكونات ويلغي التغييرات المتتابعة عند تعديل الوظائف.

النقاط الرئيسية

  • SRP — أول مبدأ من SOLID، يتطلب مسؤولية واحدة للفئة
  • سبب التغيير — المعيار الوحيد لتحديد المسؤولية في وحدة
  • انتهاك SRP يؤدي إلى كود مترابط يصعب اختباره وتوسيعه
  • تطبيق المبدأ يبسط إعادة الهندسة ويقلل خطر أخطاء الارتداد
  • SRP في التطوير المحمول يساعد في فصل منطق UI وقواعد الأعمال والتعامل مع البيانات

ما هو SRP (Single Responsibility Principle)؟

SRP (Single Responsibility Principle) — مبدأ المسؤولية الواحدة، وهو ينص على: يجب أن يكون لكل فئة أو وحدة سبب واحد فقط للتغيير. هذا لا يعني أن الفئة يجب أن تقوم بعملية واحدة فقط. الأمر يتعلق بمجموعة من الإجراءات المترابطة التي تجمعها مسؤولية واحدة أمام فاعل واحد.

أعاد روبرت مارتين صياغة SRP بمصطلحات الفاعلين: يجب أن تتغير الفئة فقط بطلب طرف واحد مهتم أو مجموعة واحدة من الأشخاص. إذا كان فاعلان مختلفان يطلبان تغييرات في نفس الفئة، فإن المسؤولية مقسمة بشكل غير صحيح.

على سبيل المثال، فئة Employee التي تحسب الراتب (طلب قسم المحاسبة) وتنشئ التقارير (طلب الإدارة) تنتهك SRP. قد يؤثر تغيير قواعد الحساب على إنشاء التقارير والعكس.

التعريف الرسمي لـ SRP

يجب أن يكون للوحدة سبب واحد وحيد للتغيير. يتم تحديد سبب التغيير بواسطة فاعل — شخص أو نظام يبدأ المتطلب. إذا كانت متطلبات من فاعلين مختلفين تؤدي إلى تغيير وحدة واحدة، فإن الوحدة تنتهك SRP.

يجعل مفهوم الفاعل SRP أداة عملية للتحليل المعماري، وليس توصية مجردة. عند تصميم نظام، يكفي أن تسأل: «من سيطلب تغيير هذا الكود؟» — إذا كانت الإجابة تتضمن أكثر من طرف واحد مهتم، فإن المسؤولية يجب أن تقسم.

كيف يعمل مبدأ المسؤولية الواحدة

تتم تطبيق المسؤولية الواحدة من خلال تجميع الطرق التي تتغير لسبب واحد. تصبح الفئة نقطة تجمع للمنطق المترابط لا سكينًا سويسرية لكل الحالات. يبسط هذا فهم الكود: يرى المطور الفئة ويفهم فورًا غرضها.

يعتمد آلية عمل SRP على قاعدة محور التغيير الواحد. إذا كانت الوظيفة قابلة للتغيير لأسباب مستقلة، فينبغي استخراجها إلى فئات منفصلة. تبنى الصلات بين هذه الفئات من خلال التركيب أو التفويض.

يظهر انتهاك SRP في كائنات عملاقة تحتوي على عشرات الطرق التي تعمل مع بيانات مختلفة. يصعب اختبار هذه الفئات — فاختبار طريقة واحدة يتطلب إعداد البيئة لجميع الطرق الأخرى. قد يكسر تغيير مسؤولية واحدة أخرى، مما يجعل الكود هشًا.

في الممارسة، يساعد SRP المطورين في الإجابة على سؤال «أين يوجد هذا الكود؟». إذا كانت كل مسؤولية منفصلة في فئتها الخاصة، فإن العثور على الملف المناسب يستغرق ثوانٍ. في مشروع Android بمعمارية MVVM، يعني هذا أن UserViewModel يكون مسؤولًا فقط عن حالة شاشة المستخدم، وUserRepository عن جلب البيانات. المطور الذي يبحث عن منطق التخزين المؤقت يذهب إلى UserCacheRepository، وليس إلى ViewModel. هذا التنظيم يسرع انضمام أعضاء الفريق الجدد ويقلل عدد الأخطاء أثناء إعادة الهندسة.

لماذا SRP مهم في التطوير المحمول

يفرض التطوير المحمول متطلبات خاصة بالنسبة لوحداتية الكود. غالبًا ما تصبح Fragment في Android أو ViewController في iOS نقاط جذب للمنطق: معالجة النقرات، استدعاء API، تحليل الاستجابة، تحديث UI — كله في فئة واحدة. SRP يتطلب فصل هذه المسؤوليات.

في معمارية Android، SRP مدمج في توصيات Google بشأن Jetpack: ViewModel مسؤول عن حالة الشاشة، Repository عن البيانات، UseCase عن منطق الأعمال. لكل مكون سبب واحد للتغيير. في تطوير iOS، تتبع أنماط MVVM وCoordinator نفس المنطق.

يحقق الالتزام بـ SRP في المشاريع المحمولة فوائد قابلة للقياس: تقليل حجم الفئات بنسبة 40-60%، تقليص الوقت في مراجعات الكود وتقليل أخطاء الارتداد عند إضافة وظائف جديدة. الوحدات المعزولة أسهل في تغطيتها باختبارات الوحدة وإعادة استخدامها في شاشات أخرى.

تأثير SRP على الاختبارات

تتطلب اختبارات الوحدة للفئات المتوافقة مع SRP عددًا أقل من كائنات المحاكاة وإعدادات أقل. إذا كان للفئة مسؤولية واحدة، فإن تبعياتها محدودة. يختبر الاختبار سلوكًا واحدًا، وليس مزيجًا من سيناريوهات غير مرتبطة.

وفقًا لتقرير Google Testing Blog (2023)، تظهر الفئات ذات المسؤولية الواحدة تغطية اختبارية أعلى بنسبة 35% مقارنة بالفئات التجميعية. يكتب المطورون اختبارات بشكل أكبر للوحدات الصغيرة المفهومة.

أمثلة SRP في Android وiOS

لننظر إلى فئة Android نموذجية تنتهك SRP — تقوم بتحميل البيانات وتحليل الاستجابة وتحديث UI. بعد إعادة الهندسة، كل مسؤولية منفصلة في مكونها الخاص.

kotlin
// انتهاك SRP: فئة واحدة تفعل كل شيء
class BadUserProfileActivity {
    fun loadUser(userId: Int) {
        // طلب HTTP
        // تحليل JSON
        // تحديث UI
        // حفظ في قاعدة البيانات
    }
}

// بعد تطبيق SRP
class UserRepository {
    fun getUser(userId: Int): User
}

class UserViewModel {
    private val repo: UserRepository
    fun loadUser(userId: Int) { }
}

class UserProfileFragment {
    fun render(user: User) { }
}

مثال مماثل في iOS Swift مع فصل طبقة الشبكة والعرض:

swift
// انتهاك SRP: ViewController يدير البيانات وUI
class BadProfileViewController: UIViewController {
    func viewDidLoad() {
        // طلب URLSession
        // فك تشفير JSON
        // تحدياث label
    }
}

// بعد تطبيق SRP
protocol UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User
}

class ProfileViewModel {
    private let service: UserServiceProtocol
    func loadProfile(id: Int) { }
}

class ProfileViewController: UIViewController {
    func display(user: User) { }
}

إعادة هندسة SRP لا تعقد المعمارية — إنها تعيد توزيع المسؤولية. قد ينخفض حجم الكود بشكل على إلغاء التكرار. لكل فئة جديدة غرض واضح ويمكن تطويرها بشكل مستقل.

التركيب كبديل عن الوراثة

يساعد التركيب في الحفاظ على SRP حيث تخلق الوراثة اقترانات غير ضرورية. بدلاً من فئة أساس مع عشرات الطرق، تتلقى الفئة الفرعية مجموعة من الكائنات المتخصصة عبر المنشئ. كل كائن مسؤول عن وظيفته الخاصة.

في تطوير Android، يسمح نمط Decorator بإضافة مسؤوليات دون تعديل الفئة الأصلية. في iOS، يفصل تسلسل Middleware في طبقة الشبكة التسجيل والتخزين المؤقت والمصادقة إلى وحدات منفصلة.

انتهاكات SRP النموذجية وعواقبها

أكثر الانتهاكات شيوعًا هو الكائن العملاق: فئة تدير قاعدة البيانات وترسل الإشعارات وتنشئ التقارير وتعالج إدخالات المستخدم. تصبح هذه الفئة عنق زجاجة في المشروع: أي تغيير يتطلب اختبارات ارتداد كاملة.

في التطوير المحمول، يؤدي خلط منطق الأعمال ومنطق UI في Activity أو Fragment أو ViewController إلى انتهاك SRP. عندما يقوم onClickListener بالتحقق من البيانات واستدعاء API وتحديث ظهور الأزرار معًا — هذا انتهاك مباشر لمبدأ المسؤولية الواحدة.

تشمل عواقب انتهاك SRP: صعوبة التطوير المتوازي (تعارضات في ملف واحد)، صعوبة اختبارات الوحدة، ارتفاع تكلفة إجراء التغييرات وانخفاض قرائية الكود. تتطلب المشاريع ذات انتهاكات SRP المنتظمة 2-3 أضعاف الوقت لإضافة وظائف جديدة.

مؤشرات انتهاك SRP في الكود

يمكن تحديد انتهاك SRP من خلال علامات غير مباشرة: تتجاوز الفئة 200 سطر، تستورد وحدات من طبقات مختلفة (UI + network + database)، لديها أكثر من 5 طرق عامة من مواضيع مختلفة. مقياس التماسك هو مؤشر إحصائي: الاختراق المنخفض للطرق داخل الفئة يشير إلى انتهاك SRP.

لكشف انتهاكات SRP، استخدم أدوات التحليل الثابت: لـ Android — Detekt مع قاعدة TooManyFunctions، ولـ iOS — SwiftLint مع قاعدة file_length. تُسلط هذه الأدوات الضوء على الفئات التي تتجاوز حدود الحجم والتعقيد.

تتم إعادة هندسة الفئات المنتهكة لـ SRP من خلال Extract Class أو Extract Delegate: يتم استخراج مجموعة من الطرق المترابطة إلى فئة منفصلة، وتفوض الفئة الأصلية بالاستدعاءات لها. تحول التطبيقات التدريجية لهذه إعادات الهندسة الكائن العملاق إلى مجموعة من الوحدات ضعيفة الاقتران، كل واحدة بمسؤولية واحدة. يسمح هذا النهج بتحسين المعمارية دون إيقاف التطوير — تتم إعادة الهندسة تكراريًا، وحدة واحدة في كل مرة.

الأسئلة الشائعة

هل يعني SRP أن الفئة يجب أن تحتوي على طريقة واحدة؟

لا. SRP ليس عن عدد الطرق، بل عن عدد أسباب التغيير. يمكن أن يكون للفئة عشرات الطرق إذا كانت تخدم مسؤولية واحدة أمام فاعل واحد. طريقة واحدة هي النقيض المقابل، التي تؤدي إلى تجزئة مفرطة للكود.

ما الفرق بين SRP ومبدأ الواجب الواحد؟

هما نفس المبدأ. يترجم Single Responsibility Principle إلى «المسؤولية الواحدة» و«الواجب الواحد». يعكس مصطلح «المسؤولية» الجوهر بشكل أكثر دقة: الأمر يتعلق بالمسؤولية أمام فاعل، وليس بوظيفة تقنية.

كيف يرتبط SRP بنمط Repository؟

Repository هو نتيجة مباشرة لتطبيق SRP على طبقة البيانات. بدلاً من نشر منطق الوصول إلى البيانات عبر ViewModel أو UseCase، يتحمل Repository مسؤولية واحدة: توفير البيانات مع تجريد المصدر. هذا تنفيذ كلاسيكي لـ SRP في المعمارية المحمولة.

هل يمكن أن يكون للفئة المتوافقة مع SRP اعتمادات على فئات أخرى؟

نعم، SRP لا يمنع الاعتمادات. يمكن للفئة ذات المسؤولية الواحدة أن تفوض بجزء من العمل لفئات أخرى عبر التركيب. المهم أن هذه المهام المفوضة تكون جزءًا من نفس المسؤولية، وليس سببًا مستقلاً للتغيير.

كيف أتحقق من التزام الفئة بـ SRP؟

اطرح السؤال: «ما الفاعلون الذين قد يطلبون تغيير هذه الفئة؟» إذا كانت الإجابة تتضمن أكثر من فاعل واحد، فإن SRP منتهك. وبالإضافة: حاول وصف غرض الفئة بجملة واحدة بدون اداة «و». إذا لم تستطع، فالفئة تفعل الكثير.

الملخص

  • SRP (Single Responsibility Principle) — أول مبدأ من SOLID، يتطلب سببًا واحدًا لتغيير الفئة
  • سبب التغيير يحدده فاعل — شخص أو نظام يبدأ متطلبًا للوحدة
  • انتهاك SRP يؤدي إلى God Class وانخفاض قابلية الاختبار وارتفاع تكاليف التغيير
  • في التطوير المحمول SRP يفصل منطق UI ومنطق الأعمال ومعالجة البيانات في مكونات منفصلة
  • التركيب يساعد في الحفاظ على SRP بشكل أفضل من الوراثة عن طريق التفويض لكائنات متخصصة
  • أدوات التحليل الثابت (Detekt، SwiftLint) تكتشف تلقائيًا انتهاكات SRP المحتملة
  • اختبارات الوحدة للفئات المتوافقة مع SRP تتطلب عددًا أقل من كائنات المحاكاة وتظهر تغطية أعلى للكود

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا