Back home

أنماط بنية iOS

الأنماط المعمارية لنظام iOS

##أنماط معمارية iOS الأنماط المعمارية لنظام iOS

إزالة الغموض عن MVC وMVP وMVVM وVIPER إزالة الغموض عن MVC وMVP وMVVM وVIPER

لا تفوت [خارطة طريق مطوري iOS] (https://github.com/BohdanOrlov/iOS-Developer-Roadmap) لعام 2018!

محدث: الشرائح التي قدمتها في NSLondon متاحة [هنا] (http://slides.com/borlov/arch/fullscreen). محدث: يمكن العثور على الشرائح التي قدمتها في NSLondon هنا.

هل تشعر بالغرابة أثناء إجراء MVC على نظام iOS؟ هل لديك شكوك حول التحول إلى MVVM؟ سمعت عن VIPER، ولكن لست متأكدا إذا كان يستحق ذلك؟ هل تشعر بالغرابة عند استخدام MVC في iOS؟ هل لديك أسئلة حول التبديل إلى MVVM؟ هل سمعت عن Viper، لكن لست متأكدًا مما إذا كان يستحق ذلك؟

استمر في القراءة، وستجد إجابات للأسئلة أعلاه، إذا كنت لا تتردد في تقديم شكوى في التعليقات. تابع القراءة وستجد الإجابات على الأسئلة أعلاه، وإذا لم تجدها، فلا تتردد في تقديم شكوى في التعليقات.

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

قد يكون إتقان أنماط التصميم أمرًا مسببًا للإدمان، لذا احذر: قد ينتهي بك الأمر إلى طرح المزيد من الأسئلة على نفسك الآن عما كانت عليه قبل قراءة هذه المقالة، مثل هذه: قد يكون إتقان أنماط التصميم أمرًا مسببًا للإدمان، لذا كن حذرًا: ربما تطرح على نفسك أسئلة أكثر الآن مما كانت عليه قبل قراءة هذه المقالة، مثل:

من المفترض أن يمتلك طلب الشبكة: النموذج أم المتحكم؟ من يجب أن يمتلك طلبات الشبكة: النموذج أم وحدة التحكم؟

كيف يمكنني تمرير نموذج إلى نموذج عرض لطريقة عرض جديدة؟ كيف يمكنني تمرير نموذج إلى نموذج العرض الخاص بطريقة عرض جديدة؟

من يقوم بإنشاء وحدة VIPER جديدة: جهاز التوجيه أو مقدم العرض؟ من أنشأ وحدة Viper الجديدة: جهاز التوجيه أم البرنامج التجريبي؟

لماذا الاهتمام باختيار الهندسة المعمارية؟

لماذا يجب أن تهتم باختيار الهندسة المعمارية؟

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

  • هذه الفئة هي فئة فرعية UIViewController. هذه الفئة هي فئة فرعية UIViewController.

  • يتم تخزين بياناتك مباشرة في UIViewController يتم تخزين بياناتك مباشرة في UIViewController

  • واجهات المستخدم الخاصة بك لا تفعل شيئًا تقريبًا uiview الخاص بك لا يفعل شيئًا تقريبًا

  • النموذج عبارة عن بنية بيانات غبية النموذج عبارة عن بنية بيانات غبية

  • اختبارات وحدتك لا تغطي شيئًا اختبارات وحدتك لا تغطي أي شيء

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

دعونا نحدد ميزات الهندسة المعمارية الجيدة: دعونا نحدد خصائص العمارة الجيدة:

  1. التوزيع المتوازن للمسؤوليات بين الجهات ذات الأدوار الصارمة. التوزيع المتوازن للمسؤوليات بين الجهات ذات الأدوار الصارمة.

  2. عادةً ما تأتي القابلية للاختبار من الميزة الأولى (ولا تقلق: إنها سهلة مع البنية المناسبة). عادةً ما تأتي القابلية للاختبار من الميزة الأولى (لا تقلق: إنها سهلة مع البنية المناسبة).

  3. سهولة الاستخدام وتكلفة صيانة منخفضة. سهل الاستخدام وتكلفة صيانة منخفضة.

لماذا التوزيع؟

لماذا وزعت؟

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

لماذا القابلية للاختبار؟

أسباب قابلية الاختبار؟

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

لماذا سهولة الاستخدام؟

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

أساسيات MV(X).

ضروريات MV (X)

في الوقت الحاضر لدينا العديد من الخيارات عندما يتعلق الأمر بأنماط التصميم المعماري: الآن، عندما يتعلق الأمر بأنماط التصميم المعماري، لدينا العديد من الخيارات:

*إم في سي *أفضل لاعب *مففم *فايبر

يفترض الثلاثة الأوائل وضع كيانات التطبيق في واحدة من ثلاث فئات: تتمثل الافتراضات الثلاثة الأولى في تقسيم كيانات التطبيق إلى ثلاث فئات:

  • النماذج - مسؤولة عن بيانات المجال أو طبقة الوصول إلى البيانات التي تعالج البيانات، فكر في فئات “الشخص” أو “PersonDataProvider”. النموذج - طبقة الوصول إلى البيانات المسؤولة عن بيانات المجال أو البيانات التشغيلية، ضع في اعتبارك فئات “الشخص” أو “PersonDataProvider”.

  • طرق العرض — مسؤول عن طبقة العرض التقديمي (GUI)، وبالنسبة لبيئة iOS فكر في كل شيء يبدأ بالبادئة “UI”. عرض - مسؤول عن طبقة العرض التقديمي (GUI)، بالنسبة لبيئات iOS، فكر في كل شيء يبدأ بـ “UI”.

  • وحدة التحكم/المقدم/نموذج العرض - الغراء أو الوسيط بين النموذج والعرض، المسؤول بشكل عام عن تغيير النموذج من خلال التفاعل مع إجراءات المستخدم التي يتم تنفيذها على العرض وتحديث العرض بالتغييرات من النموذج. وحدة التحكم/المقدم/ViewModel - الرابط أو الوسيط بين النموذج وطريقة العرض، وعادةً ما يكون مسؤولاً عن تغيير النموذج استجابةً لإجراءات المستخدم في طريقة العرض وتحديث العرض بالتغييرات من النموذج.

إن تقسيم الكيانات يسمح لنا بما يلي: يسمح لنا تقسيم الكيان بما يلي:

  • فهمهم بشكل أفضل (كما نعلم بالفعل) فهمهم بشكل أفضل (كما نعلم بالفعل)

  • إعادة استخدامها (ينطبق في الغالب على العرض والنموذج) إعادة استخدامها (ينطبق بشكل أساسي على طرق العرض والنماذج)

  • اختبارها بشكل مستقل اختبار مستقل

لنبدأ بأنماط MV(X) ونعود إلى VIPER لاحقًا. لنبدأ بوضع MV(X) ونعود إلى VIPER لاحقًا.

###إم في سي

كيف كان الحال من قبل

كيف كان الأمر في الماضي

قبل مناقشة رؤية Apple لـ MVC، دعنا نلقي نظرة على [الرؤية التقليدية] (https://en.wikipedia.org/wiki/Model–view–controller). قبل مناقشة رؤية Apple لـ MVC، دعنا نلقي نظرة على MVC التقليدية. تقليديMVC

في هذه الحالة، يكون العرض عديم الحالة. يتم تقديمه ببساطة بواسطة وحدة التحكم بمجرد تغيير النموذج. فكر في إعادة تحميل صفحة الويب بالكامل بمجرد الضغط على الرابط للتنقل في مكان آخر. على الرغم من أنه من الممكن تنفيذ MVC التقليدي في تطبيق iOS، إلا أنه ليس له معنى كبير بسبب المشكلة المعمارية - جميع الكيانات الثلاثة مقترنة بإحكام، وكل كيان ** يعرف ** عن الكيانين الآخرين. وهذا يقلل بشكل كبير من إمكانية إعادة استخدام كل منها - وهذا ليس ما تريده في تطبيقك. لهذا السبب، فإننا نتخطى حتى محاولة كتابة مثال MVC أساسي. في هذه الحالة، يكون العرض عديم الحالة. بمجرد تغيير النموذج، تقوم وحدة التحكم بعرضه ببساطة. عندما تضغط على رابط للانتقال إلى مكان آخر، فكر في إعادة تحميل صفحة الويب بالكامل. في حين أنه من الممكن تنفيذ MVC التقليدي في تطبيق iOS، إلا أن هذا ليس له معنى كبير بسبب المشكلات المعمارية - جميع الكيانات الثلاثة مقترنة بإحكام، وكل كيان يعرف عن الكيانين الآخرين. وهذا يقلل بشكل كبير من إمكانية إعادة استخدامها - وهو أمر لا تريده في تطبيقك. لهذا السبب، قمنا بتخطي كتابة أمثلة MVC الأساسية.

لا يبدو أن MVC التقليدي قابل للتطبيق على تطوير iOS الحديث. يبدو أن MVC التقليدي لا يعمل مع تطوير iOS الحديث.

MVC من Apple

أبل إم في سي

التوقع

التوقع

CocoaMVC

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

من الناحية النظرية، يبدو الأمر واضحًا جدًا، لكنك تشعر أن هناك خطأ ما، أليس كذلك؟ حتى أنك سمعت أشخاصًا يختصرون MVC على أنه Massive View Controller. علاوة على ذلك، أصبح عرض تفريغ وحدة التحكم موضوعًا مهمًا لمطوري iOS. لماذا يحدث هذا إذا أخذت Apple للتو MVC التقليدي وقامت بتحسينه قليلاً؟ من الناحية النظرية، يبدو هذا بسيطًا، لكنك تشعر أن هناك خطأ ما، أليس كذلك؟ لقد سمعت أيضًا أشخاصًا يختصرون MVC لـ Large View Controller. بالإضافة إلى ذلك، أصبح عرض تفريغ وحدة التحكم موضوعًا مهمًا لمطوري iOS. لماذا يحدث هذا إذا أخذت Apple للتو MVC التقليدي وأدخلت بعض التحسينات عليه؟

MVC من Apple

أبل إم في سي

الواقع

الواقع

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

كم مرة رأيت كودًا مثل هذا: كم مرة رأيت كودًا مثل هذا:

  
var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(user)

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

Cocoa MVC لا يتم اختصاره بشكل معقول على أنه جهاز التحكم في العرض الضخم. تم تبسيط Cocoa MVC بشكل معقول إلى وحدات تحكم العرض الكبيرة.

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

دعونا نلقي نظرة على مثال الملعب البسيط: دعونا نلقي نظرة على مثال بسيط للملعب:

محدث: راجع أمثلة التعليمات البرمجية المحدثة بواسطة Wasin Thonkaew محدث: راجع مثال التعليمات البرمجية المحدث لـ Wasin Thonkaew


import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

class GreetingViewController : UIViewController { // View + Controller
    var person: Person!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }
    
    func didTapButton(button: UIButton) {
        let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
        self.greetingLabel.text = greeting
        
    }
    // layout code goes here
}
// Assembling of MVC
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
view.person = model;

مثال MVC

يمكن إجراء تجميع MVC في وحدة تحكم العرض التقديمي يمكن إجراء تجميع MVC في وحدة تحكم العرض التقديمي

لا يبدو هذا قابلاً للاختبار، أليس كذلك؟ يمكننا نقل إنشاء التحية إلى فئة GreetingModel الجديدة واختبارها بشكل منفصل، ولكن لا يمكننا اختبار أي منطق عرض تقديمي (على الرغم من عدم وجود الكثير من هذا المنطق في المثال أعلاه) داخل GreetingViewController دون استدعاء الأساليب المرتبطة بـ UIView مباشرة (viewDidLoad، didTapButton) مما قد يتسبب في تحميل جميع طرق العرض، وهذا أمر سيء بالنسبة لاختبار الوحدة. هذا لا يبدو ممكنا جدا، أليس كذلك؟ يمكننا استبدال Greetings في فئة GreetingModel الجديدة واختبارها بشكل فردي، لكننا لن نكون قادرين على اختبار أي منطق للعرض التقديمي (على الرغم من عدم وجود الكثير من هذا المنطق في المثال أعلاه) في GreetingViewController دون استدعاء الطريقة ذات الصلة بـ UIView مباشرة (viewDidLoad didTapButton) والتي قد تتسبب في تحميل جميع طرق العرض، وهو ما لا يفضي إلى اختبار الوحدة.

في الواقع، تحميل واختبار واجهات المستخدم على جهاز محاكاة واحد (مثل iPhone 4S) لا يضمن أنها ستعمل بشكل جيد على الأجهزة الأخرى (مثل iPad)، لذلك أوصي بإزالة “التطبيق المضيف” من التكوين المستهدف لاختبار الوحدة وتشغيل اختباراتك دون تشغيل تطبيقك على جهاز محاكاة. في الواقع، تحميل واختبار UIView على جهاز محاكاة واحد (مثل iPhone 4S) لا يضمن أنه سيعمل بشكل صحيح على الأجهزة الأخرى (مثل iPad)، لذلك أوصي بإزالة “التطبيق المضيف” من التكوين المستهدف لاختبار الوحدة وتشغيل الاختبارات مع التطبيق الذي يعمل على جهاز المحاكاة.

التفاعلات بين العرض ووحدة التحكم ليست قابلة للاختبار حقًا من خلال اختبارات الوحدة لا يمكن فعليًا اختبار التفاعل بين العرض ووحدة التحكم من خلال اختبارات الوحدة

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

  • **التوزيع ** — العرض والنموذج منفصلان في الواقع، لكن العرض ووحدة التحكم مقترنان بإحكام. التوزيع - طرق العرض والنماذج منفصلة بشكل فعال، ولكن طرق العرض ووحدات التحكم مقترنة بإحكام.

  • قابلية الاختبار — نظرًا للتوزيع السيئ، فمن المحتمل أنك ستختبر النموذج الخاص بك فقط. قابلية الاختبار - لا يجوز لك اختبار النموذج الخاص بك إلا بسبب سوء التوزيع.

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

يعد Cocoa MVC أفضل نمط معماري من حيث سرعة التطوير. من حيث سرعة التطوير، يعد Cocoa MVC أفضل نمط معماري.

###أفضل لاعب

تم الوفاء بوعود Cocoa MVC

لقد تم الوفاء بوعد Cocoa MVC

متغير العرض السلبي لـ MVP

ألا يبدو تمامًا مثل MVC الخاص بشركة Apple؟ نعم، إنه كذلك، واسمه MVP (متغير العرض السلبي). لكن انتظر لحظة… هل هذا يعني أن MVC من Apple هو في الواقع MVP؟ لا، ليس كذلك، لأنه إذا كنت تتذكر، فإن العرض مقترن بإحكام بـ وحدة التحكم، في حين أن وسيط MVP، المقدم، لا علاقة له بدورة حياة وحدة التحكم في العرض، ويمكن الاستهزاء بـ العرض بسهولة، لذلك لا يوجد كود تخطيط في المقدم على الإطلاق، ولكنه مسؤول عن تحديث العرض بالبيانات والحالة. ألا يبدو مثل MVC الخاص بشركة Apple؟ نعم هو كذلك، واسمه MVP (Passive View Variant). لكن مهلا، هل هذا يعني أن MVC الخاص بشركة Apple هو في الواقع MVP؟ لا، ليس كذلك، لأنه إذا كنت تتذكر، فإن العرض مقترن بإحكام بوحدة التحكم، ووسيط MVP، المقدم، ليس له علاقة بدورة حياة وحدة تحكم العرض، يمكن الاستهزاء بالعرض بسهولة، لذلك لا يوجد رمز تخطيط في المقدم، ولكنه مسؤول عن تحديث العرض بالبيانات والحالة.

ماذا لو أخبرتك أن UIViewController هو العرض. إذا أخبرتك أن UIViewController عبارة عن طريقة عرض.

فيما يتعلق بـ MVP، فإن الفئات الفرعية UIViewController هي في الواقع Views وليست Presenters. يوفر هذا التمييز قابلية اختبار رائعة، والتي تأتي على حساب سرعة التطوير، لأنه يتعين عليك إنشاء بيانات يدوية وربط الأحداث، كما ترون من المثال: بقدر ما يتعلق الأمر بـ MVP، فإن الفئات الفرعية UIViewController هي في الواقع طرق عرض وليست مقدمين. يوفر هذا التمييز قابلية اختبار ممتازة، ولكنه يأتي على حساب سرعة التطوير لأنه يتعين عليك القيام بالبيانات اليدوية وربط الأحداث، كما ترون من المثال:


import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

protocol GreetingViewPresenter {
    init(view: GreetingView, person: Person)
    func showGreeting()
}

class GreetingPresenter : GreetingViewPresenter {
    unowned let view: GreetingView
    let person: Person
    required init(view: GreetingView, person: Person) {
        self.view = view
        self.person = person
    }
    func showGreeting() {
        let greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
        self.view.setGreeting(greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var presenter: GreetingViewPresenter!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }
    
    func didTapButton(button: UIButton) {
        self.presenter.showGreeting()
    }
    
    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }
    
    // layout code goes here
}
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter

مثال أفضل لاعب

ملاحظة هامة بخصوص التجميع

ملاحظات هامة حول التجميع

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

دعونا نلقي نظرة على الميزات لـ MVP: دعونا نلقي نظرة على خصائص MVP:

  • التوزيع — لدينا معظم المسؤوليات مقسمة بين المقدم والنموذج، مع وجهة نظر غبية جدًا (في المثال أعلاه، النموذج غبي أيضًا). التوزيع - قمنا بتوزيع معظم المسؤوليات بين المقدم والنموذج، واستخدمنا طرق عرض غبية جدًا (في المثال أعلاه، كان النموذج غبيًا أيضًا).

  • قابلية الاختبار — ممتازة، ويمكننا اختبار معظم منطق الأعمال بسبب طريقة العرض الغبية. القابلية للاختبار - جيد جدًا، يمكننا اختبار معظم منطق الأعمال بفضل وجهات النظر الغبية.

  • سهولة الاستخدام— في مثالنا البسيط وغير الواقعي، يتم مضاعفة كمية التعليمات البرمجية مقارنة بـ MVC، ولكن في الوقت نفسه، تكون فكرة MVP واضحة جدًا. سهولة الاستخدام - في مثالنا البسيط غير الواقعي، يتم مضاعفة كمية التعليمات البرمجية مقارنة بـ MVC، ولكن في نفس الوقت، يكون مفهوم MVP واضحًا جدًا.

يعني MVP في iOS قابلية اختبار رائعة والكثير من التعليمات البرمجية. يعني MVP في iOS قابلية اختبار رائعة والكثير من التعليمات البرمجية.

###أفضل لاعب اللاعب الأكثر قيمة

مع الارتباطات والأبواق

بحبل وقبعة

هناك النكهة الأخرى لـ MVP - أفضل لاعب في وحدة التحكم المشرف. يتضمن هذا المتغير ربطًا مباشرًا بـ العرض والنموذج بينما لا يزال المقدم (وحدة التحكم المشرف) يتعامل مع الإجراءات من العرض ويكون قادرًا على تغيير العرض. هناك نكهة أخرى لـ MVP - أفضل لاعب إشرافي. يتضمن هذا المتغير الربط المباشر للعرض والنموذج، بينما لا يزال المقدم (وحدة التحكم في المراقبة) يتعامل مع العمليات من العرض ويكون قادرًا على تغيير العرض.

الإشراف على متغير المقدم لـ MVP

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

كما هو الحال مع MVC التقليدي، لا أرى فائدة من كتابة مثال للبنية المعيبة. كما هو الحال مع MVC التقليدي، لا أعتقد أنه من الضروري كتابة أمثلة لبنية معيبة.

###MVVM

الأحدث والأكبر من نوع MV(X). أحدث وأكبر فئة MV(X).إن MVVM هو الأحدث من نوع MV(X)، لذلك دعونا نأمل أن يكون قد ظهر مع الأخذ في الاعتبار المشكلات التي كانت MV(X) تواجهها سابقًا. MVVM هو أحدث نوع MV(X)، لذلك دعونا نأمل أن يتم طرحه مع الأخذ في الاعتبار المشكلات التي واجهتها MV(X) من قبل.

من الناحية النظرية يبدو Model-View-ViewModel جيدًا جدًا. العرض و النموذج مألوفان لدينا بالفعل، ولكن أيضًا الوسيط، والذي يتم تمثيله بـ نموذج العرض. من الناحية النظرية، يبدو نموذج عرض النموذج جيدًا جدًا. إن وجهات النظر والنماذج مألوفة لنا بالفعل، وكذلك الوساطات، ممثلة كنماذج عرض.

إنه مشابه جدًا لـ MVP: هذا مشابه جدًا لـ MVP:

  • يعامل MVVM وحدة التحكم في العرض على أنها عرض يعامل MVVM وحدات تحكم العرض كطرق عرض

  • لا يوجد اقتران محكم بين العرض والنموذج لا يوجد اقتران وثيق بين العرض والنموذج

بالإضافة إلى ذلك، فهو ملزم مثل الإصدار المشرف من MVP؛ ومع ذلك، هذه المرة ليس بين العرض والنموذج، ولكن بين العرض ونموذج العرض. علاوة على ذلك، فهو ملزم مثل الإصدار التنظيمي لـ MVP؛ ومع ذلك، هذه المرة ليس بين العرض والنموذج، ولكن بين العرض ونموذج العرض.

إذن ما هو نموذج العرض في واقع iOS؟ إنه في الأساس تمثيل UIKit مستقل لـ العرض وحالته. يستدعي نموذج العرض تغييرات في النموذج ويقوم بتحديث نفسه باستخدام النموذج المحدث، وبما أن لدينا رابطًا بين العرض ونموذج العرض، يتم تحديث الأول وفقًا لذلك. ما هو نموذج العرض في iOS؟ إنه في الأساس تمثيل UIKit المستقل للعرض وحالته. يستدعي نموذج العرض التغييرات في النموذج ويحدث نفسه بالنموذج المحدث، وبما أن لدينا ارتباطًا بين العرض ونموذج العرض، فسيتم تحديث النموذج الأول وفقًا لذلك.

الارتباطات

ملزمة

لقد ذكرتها بإيجاز في الجزء MVP، ولكن دعونا نناقشها قليلاً هنا. يتم إخراج الروابط من الصندوق لتطوير OS X، ولكننا لا نتوفر عليها في صندوق أدوات iOS. بالطبع لدينا KVO والإشعارات، لكنها ليست مريحة مثل الارتباطات. لقد ذكرتها بإيجاز في قسم MVP، ولكن دعونا نناقشها هنا. يتم إنشاء الروابط خارج الصندوق لتطوير OS X، ولكننا لا نتوفر عليها في صندوق أدوات iOS. بالطبع، لدينا KVO والإخطارات، لكنها ليست مريحة مثل ملزمة.

لذا، بشرط ألا نرغب في كتابتها بأنفسنا، لدينا خياران: لذا، إذا كنا لا نريد أن نكتبها بأنفسنا، فلدينا خياران:

  • إحدى مكتبات الربط المعتمدة على KVO مثل RZDataBinding أو SwiftBond إحدى مكتبات الربط المعتمدة على KVO مثل RZDataBinding أو SwiftBond

  • وحوش البرمجة التفاعلية الوظيفية واسعة النطاق مثل ReactiveCocoa أو RxSwift أو PromiseKit. أدوات برمجة تفاعلية كاملة المواصفات مثل ReactiveCocoa أو RxSwift أو وعد eKit.

في الواقع، في الوقت الحاضر، إذا سمعت “MVVM” - فأنت تفكر في ReactiveCocoa، والعكس صحيح. على الرغم من أنه من الممكن إنشاء MVVM باستخدام الارتباطات البسيطة، إلا أن ReactiveCocoa (أو الأشقاء) سيسمح لك بالحصول على معظم MVVM. في الواقع، في الوقت الحاضر، إذا سمعت “MVVM”، فإنك تفكر في ReactiveCocoa، والعكس صحيح. في حين أنه من الممكن إنشاء MVVM بربطات بسيطة، فإن ReactiveCocoa (أو أحد الأخوة) سيسمح لك بالحصول على معظم MVVM.

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

التصحيح التفاعلي

في مثالنا البسيط، يعد إطار عمل FRF أو حتى KVO مبالغة، وبدلاً من ذلك سنطلب صراحةً من نموذج العرض التحديث باستخدام طريقة showGreeting واستخدام الخاصية البسيطة لوظيفة رد الاتصال GreetingDidChange. في مثالنا البسيط، يعد إطار عمل FRF أو حتى KVO زائدًا عن الحاجة، وبدلاً من ذلك سنطلب صراحةً من نموذج العرض التحديث باستخدام طريقة showGreeting وخاصية بسيطة باستخدام وظيفة رد الاتصال GreetingDidChange.


import UIKit

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingViewModelProtocol: class {
    var greeting: String? { get }
    var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set } // function to call when greeting did change
    init(person: Person)
    func showGreeting()
}

class GreetingViewModel : GreetingViewModelProtocol {
    let person: Person
    var greeting: String? {
        didSet {
            self.greetingDidChange?(self)
        }
    }
    var greetingDidChange: ((GreetingViewModelProtocol) -> ())?
    required init(person: Person) {
        self.person = person
    }
    func showGreeting() {
        self.greeting = "Hello" + " " + self.person.firstName + " " + self.person.lastName
    }
}

class GreetingViewController : UIViewController {
    var viewModel: GreetingViewModelProtocol! {
        didSet {
            self.viewModel.greetingDidChange = { [unowned self] viewModel in
                self.greetingLabel.text = viewModel.greeting
            }
        }
    }
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self.viewModel, action: "showGreeting", forControlEvents: .TouchUpInside)
    }
    // layout code goes here
}
// Assembling of MVVM
let model = Person(firstName: "David", lastName: "Blaine")
let viewModel = GreetingViewModel(person: model)
let view = GreetingViewController()
view.viewModel = viewModel

مثال MVVM

ونعود مرة أخرى إلى تقييمنا الميزات: العودة إلى تقييم الميزة لدينا:

  • التوزيع — هذا غير واضح في مثالنا الصغير، ولكن في الواقع، فإن وجهة نظر MVVM لديها مسؤوليات أكثر من وجهة نظر MVP. لأن الأول يقوم بتحديث حالته من نموذج العرض عن طريق إعداد الارتباطات، بينما يقوم الثاني بإعادة توجيه جميع الأحداث إلى المقدم ولا يقوم بتحديث نفسه. التوزيع - ليس الأمر واضحًا في مثالنا الصغير، ولكن في الواقع، وجهة نظر MVVM لديها مسؤوليات أكثر من وجهة نظر MVP. لأن الأول يقوم بتحديث الحالة في نموذج العرض عن طريق تعيين الارتباط، بينما يقوم الثاني فقط بإعادة توجيه جميع الأحداث إلى مقدم العرض دون تحديث نفسه.

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

  • سهل الاستخدام — يحتوي على نفس القدر من التعليمات البرمجية الموجودة في MVP في مثالنا، ولكن في التطبيق الحقيقي حيث يتعين عليك إعادة توجيه جميع الأحداث من العرض إلى المقدم وتحديث العرض يدويًا، سيكون MVVM أكثر نحافة إذا استخدمت الروابط. سهل الاستخدام - يحتوي على نفس مقدار التعليمات البرمجية الموجودة في MVP في مثالنا، ولكن في التطبيق الحقيقي، سيتعين عليك إعادة توجيه جميع الأحداث من العرض إلى مقدم العرض وتحديث العرض يدويًا، وسيكون MVVM أرق إذا تم استخدام الارتباطات.> يعد MVVM جذابًا للغاية، لأنه يجمع بين فوائد الأساليب المذكورة أعلاه، بالإضافة إلى أنه لا يتطلب تعليمات برمجية إضافية لتحديثات العرض نظرًا للارتباطات الموجودة على جانب العرض. ومع ذلك، لا تزال قابلية الاختبار في مستوى جيد. يعد MVVM جذابًا للغاية لأنه يجمع بين مزايا الطرق المذكورة أعلاه، وبسبب الارتباط من جانب العرض، فإنه لا يتطلب تعليمات برمجية إضافية لتحديث العرض. ومع ذلك، فإن قابلية الاختبار على مستوى جيد.

فايبر

تم نقل تجربة بناء LEGO إلى تصميم تطبيق iOS تم نقل تجربة بناء LEGO إلى تصميم تطبيقات iOS

VIPER هو مرشحنا الأخير، وهو أمر مثير للاهتمام بشكل خاص لأنه لا يأتي من فئة MV(X). VIPER هو مرشحنا الأخير، وهو أمر مثير للاهتمام للغاية لأنه ليس من الفئة MV(X).

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

فايبر

  • المتفاعل — يحتوي على منطق الأعمال المتعلق بالبيانات (الكيانات) أو الشبكات، مثل إنشاء مثيلات جديدة للكيانات أو جلبها من الخادم. لهذه الأغراض، ستستخدم بعض الخدمات والمديرين التي لا تعتبر جزءًا من وحدة VIPER بل هي تبعية خارجية. المتفاعل - يحتوي على منطق الأعمال المتعلق بالبيانات (الكيانات) أو الشبكة، مثل إنشاء مثيلات جديدة للكيانات أو استردادها من الخادم. لهذه الأغراض، ستستخدم بعض الخدمات والمديرين الذين لا يعتبرون جزءًا من وحدة VIPER ولكن تبعية خارجية.

  • المقدم يعني — يحتوي على منطق الأعمال المتعلق بواجهة المستخدم (ولكنه مستقل عن UIKit)، ويستدعي الأساليب الموجودة على المتفاعل. المقدم - يحتوي على منطق الأعمال المتعلق بواجهة المستخدم (ولكنه مستقل عن UIKit)، وطرق الاتصال على المتفاعل.

  • الكيانات تعني — كائنات البيانات البسيطة الخاصة بك، وليس طبقة الوصول إلى البيانات، لأن هذه مسؤولية المتفاعل. الكيانات - كائنات البيانات العادية الخاصة بك، وليس طبقة الوصول إلى البيانات لأن هذه مسؤولية المتفاعل.

  • جهاز التوجيه — مسؤول عن المقاطعات بين وحدات VIPER. جهاز التوجيه - مسؤول عن الفصل بين وحدات Viper.

في الأساس، يمكن أن تكون وحدة VIPER عبارة عن شاشة واحدة أو قصة المستخدم الكاملة لتطبيقك - فكر في المصادقة، والتي يمكن أن تكون شاشة واحدة أو عدة شاشات مرتبطة ببعضها. ما مدى صغر حجم كتل “LEGO” الخاصة بك؟ - الأمر متروك لك. في الأساس، يمكن أن تكون وحدة VIPER شاشة واحدة أو قصة المستخدم الكاملة للتطبيق - فكر في المصادقة، يمكن أن تكون شاشة واحدة أو عدة شاشات مرتبطة. ما مدى صغر حجم مكعبات “LEGO” الخاصة بك؟ - الأمر متروك لك.

إذا قارناها بالنوع MV(X)، فسنرى بعض الاختلافات في توزيع المسؤوليات: إذا قارنا ذلك بالنوع MV(X) فسنرى بعض الاختلافات في توزيع المسؤوليات:

  • تم نقل منطق النموذج (تفاعل البيانات) إلى المتفاعل مع الكيانات كهياكل بيانات غبية. يتم تحويل منطق النموذج (تفاعل البيانات) إلى هياكل بيانات غبية تتفاعل مع الكيانات.

  • تم نقل واجبات تمثيل واجهة المستخدم الخاصة بـ وحدة التحكم/المقدم/ViewModel فقط إلى المقدم، ولكن ليس إمكانيات تغيير البيانات. يتم فقط نقل وظيفة العرض التقديمي لواجهة المستخدم الخاصة بوحدة التحكم/المقدم/نموذج العرض إلى العرض التقديمي، وليس وظيفة تعديل البيانات.

  • VIPER هو النمط الأول الذي يتناول بشكل صريح مسؤولية التنقل، والتي من المفترض أن يحلها جهاز التوجيه. VIPER هو النمط الأول الذي يتعامل بشكل صريح مع مسؤوليات التنقل، والتي يجب أن يتعامل معها جهاز التوجيه.

تمثل الطريقة الصحيحة لإجراء التوجيه تحديًا لتطبيقات iOS، وأنماط MV(X) لا تعالج هذه المشكلة ببساطة. يمثل التوجيه الصحيح تحديًا لتطبيقات iOS، ولا يمكن لنمط MV(X) حل هذه المشكلة.

لا يغطي المثال التوجيه أو التفاعل بين الوحدات، حيث أن هذه المواضيع لا تغطيها أنماط MV(X) على الإطلاق. لا يتضمن هذا المثال التوجيه أو التفاعلات بين الوحدات، حيث أن نمط MV(X) لا يغطي هذه المواضيع على الإطلاق.


import UIKit

struct Person { // Entity (usually more complex e.g. NSManagedObject)
    let firstName: String
    let lastName: String
}

struct GreetingData { // Transport data structure (not Entity)
    let greeting: String
    let subject: String
}

protocol GreetingProvider {
    func provideGreetingData()
}

protocol GreetingOutput: class {
    func receiveGreetingData(greetingData: GreetingData)
}

class GreetingInteractor : GreetingProvider {
    weak var output: GreetingOutput!
    
    func provideGreetingData() {
        let person = Person(firstName: "David", lastName: "Blaine") // usually comes from data access layer
        let subject = person.firstName + " " + person.lastName
        let greeting = GreetingData(greeting: "Hello", subject: subject)
        self.output.receiveGreetingData(greeting)
    }
}

protocol GreetingViewEventHandler {
    func didTapShowGreetingButton()
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

class GreetingPresenter : GreetingOutput, GreetingViewEventHandler {
    weak var view: GreetingView!
    var greetingProvider: GreetingProvider!
    
    func didTapShowGreetingButton() {
        self.greetingProvider.provideGreetingData()
    }
    
    func receiveGreetingData(greetingData: GreetingData) {
        let greeting = greetingData.greeting + " " + greetingData.subject
        self.view.setGreeting(greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var eventHandler: GreetingViewEventHandler!
    let showGreetingButton = UIButton()
    let greetingLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    }
    
    func didTapButton(button: UIButton) {
        self.eventHandler.didTapShowGreetingButton()
    }
    
    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }
    
    // layout code goes here
}
// Assembling of VIPER module, without Router
let view = GreetingViewController()
let presenter = GreetingPresenter()
let interactor = GreetingInteractor()
view.eventHandler = presenter
presenter.view = view
presenter.greetingProvider = interactor
interactor.output = presenter

مرة أخرى، نعود إلى الميزات: العودة إلى الميزات مرة أخرى:

  • التوزيع — مما لا شك فيه أن VIPER هو بطل توزيع المسؤوليات. التوزيع - لا شك أن الأفعى بطل في توزيع المسؤولية.

  • قابلية الاختبار — لا توجد مفاجآت هنا، توزيع أفضل — قابلية اختبار أفضل. قابلية الاختبار - لا توجد مفاجآت هنا، توزيع أفضل - قابلية اختبار أفضل.

  • سهولة الاستخدام — أخيرًا، يأتي العنصران المذكوران أعلاه في تكلفة الصيانة كما خمنت بالفعل. يجب عليك كتابة قدر كبير من الواجهة للفصول الدراسية ذات المسؤوليات الصغيرة جدًا. سهولة الاستخدام - أخيرًا، كما كنت قد خمنت، العنصران المذكوران أعلاه هما تكاليف الصيانة. عليك أن تكتب الكثير من الواجهات للفصول ذات المسؤوليات الصغيرة جدًا.

فماذا عن ليغو؟

ماذا عن ليغو؟أثناء استخدام VIPER، قد تشعر بالرغبة في بناء مبنى Empire State Building من مكعبات LEGO، وهذه إشارة إلى أن لديك مشكلة. ربما، من السابق لأوانه اعتماد VIPER لتطبيقك ويجب عليك التفكير في شيء أبسط. يتجاهل بعض الناس هذا ويواصلون إطلاق النار من المدفع على العصافير. أفترض أنهم يعتقدون أن تطبيقاتهم ستستفيد من VIPER على الأقل في المستقبل، حتى لو كانت تكلفة الصيانة الآن مرتفعة بشكل غير معقول. إذا كنت تعتقد نفس الشيء، فأنا أنصحك بتجربة Generamba – وهي أداة لإنشاء الهياكل العظمية لـ VIPER. على الرغم من أنني شخصياً أشعر وكأنني أستخدم نظام استهداف آلي للمدفع بدلاً من مجرد إطلاق النار. عند استخدام VIPER، قد تشعر وكأنك تقوم ببناء مبنى Empire State Building من الليغو، وهذا مؤشر على أن لديك مشكلة. ربما يكون من السابق لأوانه اعتماد VIPER لتطبيقك ويجب عليك التفكير في بعض الطرق الأبسط. تجاهل بعض الناس ذلك واستمروا في إطلاق النار على العصافير بمدافعهم. أفترض أنهم يعتقدون أن تطبيقهم سيستفيد من VIPER على الأقل في المستقبل، حتى لو كانت تكاليف الصيانة مرتفعة بشكل غير معقول الآن. إذا كنت تعتقد ذلك، فأنا أقترح عليك تجربة Generamba - وهي أداة لإنشاء الهيكل العظمي للثعبان السام. على الرغم من أن الأمر بالنسبة لي شخصيًا يبدو وكأنه إطلاق نار باستخدام نظام التصويب التلقائي بدلاً من استخدام مقلاع بسيط.

###الخلاصة الاستنتاج

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

ولذلك، فمن الطبيعي أن يكون هناك مزيج من البنيات في نفس التطبيق. على سبيل المثال: لقد بدأت باستخدام MVC، ثم أدركت أن شاشة واحدة معينة أصبح من الصعب جدًا صيانتها بكفاءة باستخدام MVC وتحولت إلى MVVM، ولكن لهذه الشاشة بالذات فقط. ليست هناك حاجة لإعادة بناء الشاشات الأخرى التي يعمل MVC عليها بشكل جيد بالفعل، لأن كلا البنيتين متوافقتان بسهولة. ولذلك، فمن الطبيعي أن يتم مزج البنيات في نفس التطبيق. على سبيل المثال: تبدأ بـ MVC، ثم تدرك أنه من الصعب الحفاظ على شاشة معينة بكفاءة باستخدام MVC، لذا قم بالتبديل إلى MVVM، ولكن لهذه الشاشة المحددة فقط. ليست هناك حاجة لإعادة بناء الشاشات الأخرى حيث يعمل MVC بشكل جيد بالفعل، حيث أن كلا البنيتين متوافقتان بسهولة.

اجعل كل شيء بسيطًا قدر الإمكان، ولكن ليس أبسط - ألبرت أينشتاين اجعل كل شيء بسيطًا قدر الإمكان، ولكن ليس بسيطًا قدر الإمكان. - ألبرت أينشتاين

شكرا لك على القراءة! إذا أعجبك هذا المقال، يرجى التصفيق حتى يتمكن الآخرون من قراءته أيضًا :) شكرا للقراءة! إذا أعجبك هذا المنشور، يرجى التصفيق حتى يتمكن الآخرون من قراءته أيضًا.

تابعني على Twitter لمزيد من تصميمات وأنماط iOS. تابعني على Twitter لمزيد من تصميمات وأنماط iOS.

النص الأصلي