كبح رذائل الحتمية

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

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

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

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

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

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



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



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



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



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

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

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

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



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



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

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



يجدر الحديث عن خاصية disposBag الوحيدة. كما يتضح من لقطات الشاشة ، يتم وضع كل اشتراك فيها ، وهذا ضروري للتحكم في حياتهم. وهذا يعني أن الاشتراكات صالحة طالما أن "الحقيبة" تعيش فيها ، في هذه الحالة ، بينما تعيش وحدة التحكم.

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

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

بعد المراجعة ، حان الوقت لاستخدام الملاحظة من أجل تبسيط التطوير. كيف بالضبط؟ دعنا نعود إلى فكرة الأساليب "النظيفة" ونحاول تنفيذ منطق الشاشة الصغيرة بطريقتها الوحيدة ، وللتوضيح ، سنختار طريقة إنهاء تحميل العرض (viewDidLoad). بطبيعة الحال ، إذا كانت الشاشة مكونة في IB ، فسيتعين علينا إنشاء خصائص للمنافذ ، ولكن هذا ليس مخيفًا ، لأن العناصر نفسها لا تمثل منطق الأعمال ، وبالتالي لن تؤثر بشكل كبير على تعقيد الشاشة. ولكن إذا كانت الشاشة مكونة من كود ، فيمكنك الاستغناء عن الخصائص على الإطلاق (باستثناء disposBag) ، وإنشاء عناصر في طريقتنا واستخدامها هناك. ماذا عن حتمية عناصر UIKit التي تم وصفها سابقًا؟ توفر Rx ، بالإضافة إلى النهج نفسه ، أغلفة تفاعلية لمكونات واجهة المستخدم القياسية ، بحيث يمكنك في معظم الحالات الحصول على التسلسل اللازم للأحداث على الفور. أو على العكس من ذلك ، قم بربط Observable الموجود بجدول ، على سبيل المثال ، تقديم طلب إليه حتى يتم تحديث محتواه فور اكتماله:



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

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



تساعد الهياكل المنفصلة في جعل رأس الأسلوب مقروءًا وأنيقًا ، حيث يمكن أن يكون هناك العديد من التبعيات.

من المهم أن نفهم أن الطريقة لا يجب أن تكون الطريقة الوحيدة في الكائن بالكامل ، أي جوهر استقلالهم عن بعضهم البعض. بفضل Rx ، يمكن أن تكون المدخلات والمخرجات غير متزامنة وتمثل واحدًا أو أكثر يمكن ملاحظته ، مما يوفر بُعدًا آخر لمعالجة البيانات.

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

Source: https://habr.com/ru/post/ar435724/


All Articles