كيفية تطبيق مبدأ المسؤولية الفردية في سويفت

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

هناك طريقة جيدة لمعرفة حقيقة مسؤولية الفصل وهي التفكير في قابلية التوسع.

دعونا نرى مثالا على كيفية استخدام ذلك. لكن قبل أن أقدم بعض النظرية وأشرح لماذا هذا المفهوم مهم في تصميم البرمجيات.

مقدمة لمبدأ المسؤولية الفردية

إن فكرة أن يكون لكل فصل مسؤولية واحدة في مشروع برمجي ، وتلك المسؤولية التي يتم تضمينها في فئة فريدة واحدة لها اسم: مبدأ المسؤولية الفردية

هذا هو أحد المبادئ الرئيسية الخمسة لتصميم البرامج ، حيث يحاولون ، في Object Oriented Programming ، تحديد دليل ليكون لديهم برنامج أكثر قابلية للفهم ومرونة ويمكن صيانته.

هذه المبادئ هي:

  • مبدأ المسؤولية واحد
  • فتح / إغلاق المبدأ
  • Liskov مبدأ الاستبدال
  • واجهة الفصل مبدأ
  • مبدأ الانعكاس التبعية

يتحدث مؤلف هذه المفاهيم ، روبرت سي. مارتن ، (كتب أحد أهم الكتب في هندسة البرمجيات ، Clean Code) عن "يجب أن يكون للفصل سبب واحد فقط للتغيير" ، لذلك فهو يعرف المسؤولية كسبب ل يتغيرون.

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

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

السبب في أنه من المهم الحفاظ على تركيز الفصل على مصدر قلق واحد هو أنه يجعل الفصل أكثر قوة. متابعة مع المثال السابق ، إذا كان هناك تغيير في عملية تجميع التقارير ، فهناك خطر أكبر من كسر رمز الطباعة إذا كان جزءًا من نفس الفئة. (مثال مقتطف من ويكيبيديا)

لماذا من المهم تحديد مسؤولية كل فئة بشكل صحيح

إذا حددنا فصولنا بمعرفة ما هي مسؤوليتهم في مشروعنا ، فيمكننا:

  • فهم بسهولة الوظيفة التي تقوم بها في كل جزء من التعليمات البرمجية.
  • تعديل المنطق الموجود بشكل أسرع وبالتفصيل.
  • العثور مع أقل مشاكل أصل الخلل أو السلوكيات غير المرغوب فيها.
  • مجردة المنطق في فئات مختلفة أو وحدات.
  • انقسام دون مشاكل كبيرة التطبيقات بحيث يمكن استبدالها بالكامل في وقت لاحق.
  • حدد اختبارات الوحدة حسب الفصل أو الوحدة بطريقة أكثر فاعلية ، حتى نتمكن من اختبار جزء صغير من الشفرة ، ولا نريد اختبار أكثر من ذلك.

التفكير في قابلية تحديد المسؤوليات

كما قلت من قبل ، يمكنك التفكير في قابلية الفصل لتحديد المسؤوليات. هذا بسيط مثل التفكير في أنه قد يحدث في مشروعنا أن يتم تعديل المتطلبات ، وأنظر في بنيتنا كيف ستحدث هذه التعديلات.

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

دعونا نرى مثالًا ملموسًا في Swift.

مثال سريع

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

ItemsViewController: قائمة بسيطة من العناصر

يمكنك رؤية الكود في https://github.com/fedejordan/SRPExample

للقيام بذلك ، يستخدم ItemsViewController UITableView لإظهار العناصر في قائمة. كما أننا نستخدم فئة فرعية UITableViewCell تسمى ItemTableViewCell لإظهار هذه العناصر.

ولكن دعنا نقول أننا نريد تغيير العرض ، على سبيل المثال ، مع UICollectionView. ماذا ستكون المشكلة في هذا الموقف؟

رمز العرض مقترن جدًا بمنطق بيانات العناصر. إذا غيرنا طريقة العرض ، فمن المحتمل جدًا أن نقوم بتعديل الفصل المسؤول عن العناصر أيضًا.

المشكلة الحقيقية هي في هذه السطور:

دع العنصر = العناصر [indexPath.row]

لماذا المشكلة هنا؟

لأننا نستخدم فهرس UITableView للحصول على عنصر معين في الصفيف. يجب أن نستخلص بطريقة ما أن العرض يستخدم anUITableView.

لتجنب ذلك ، سنقوم بإعادة ضبط ItemsViewController وسننقل منطق النموذج إلى فئة أخرى تسمى ItemsInteractor.

مفهوم المتفاعل له أصل في بنية VIPER. كما قيل في التعريف ، يحتوي Interactor على منطق العمل للتعامل مع كائنات النموذج (الكيانات) لتنفيذ مهمة محددة. في هذه الحالة ، يكون ItemsInteractor مسؤولاً عن استرداد المعلومات حول أي عنصر.

يمكنك الحصول على هذا الرمز في https://github.com/fedejordan/SRPExample ، في الفرع reactor_refactor.

كما نرى ، لا يعرف ItemsViewController أي شيء عن نموذج البيانات. ببساطة ، اسأل المتفاعل ما الذي يحتاجه لرسم العرض.

باستخدام هذا الأسلوب ، يمكننا ، على سبيل المثال ، تغيير عنصر نوع البيانات لأي شيء آخر ، ولن نغير سوى ItemsInteractor. سوف يسترجع نفس المعلومات (لن نغير الطرق العامة) وأخيراً ، يمكن لـ ItemsViewController الاستمرار في العمل كما هو الحال دائمًا.

لذلك ، مع هذا refactor ، إذا كنا نريد تغيير تخطيط التطبيق لدينا فقط ، فنحن ببساطة نقوم بتغيير ItemsViewController لاستخدام anUICollectionView:

ItemsViewController لقطة الشاشة ، باستخدام UICollectionView

يمكنك رؤية النتيجة النهائية في https://github.com/fedejordan/SRPExample، collection_view_refactor branch.

هل قمنا بتغيير شيء في ItemsInteractor؟ فقط لا شئ. لقد قمنا ببساطة بتغيير العرض التقديمي لهذه الميزة.

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

خاتمة

يبدو أننا لم نفعل الكثير من العمل. لقد أزلنا منطق العرض ببساطة من نموذج البيانات. هل كان حقا يستحق كل هذا العناء؟ في هذا المثال ، ربما لم يكن الأمر مفيدًا جدًا ، في الواقع ، ربما نحتاج ببساطة إلى قائمة وليس إلى شيء آخر.

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

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

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

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

شكرا جزيلا لقراءة هذا المنصب!

اتمنى ان تكون قد استمتعت به. يمكنك إرسال أي رسالة أو اقتراح حول الموضوعات في قسم التعليقات ، أو ببساطة أرسل لي رسالة بريد إلكتروني إلى fedejordan99@gmail.com