سلسلة Swift Concurrency 08|تنظيم التعليمات البرمجية غير المتزامن في المشاريع الحقيقية
الأمر الصعب حقًا هو ما إذا كان الارتباط غير المتزامن بأكمله يمكن أن يظل واضحًا لفترة طويلة.
عندما يكون هناك طلب واحد أو طلبان غير متزامنين في المشروع، فإن الكثير من التعليمات البرمجية لا تبدو سيئة للغاية. عادة ما يحدث مستجمع المياه الحقيقي عندما تحدث المواقف التالية في وقت واحد:
- تحتوي الصفحة نفسها على التحميل الأول والتحديث المنسدل وإعادة المحاولة وتبديل التصفية
- يجب مشاركة ذاكرة التخزين المؤقت المحلية والطلبات البعيدة معًا
- عند مغادرة الصفحة قم بإلغاء بعض المهام واحتفظ بالأخرى
- وحدات متعددة تشترك في بعض الموارد المتزامنة
في هذا الوقت، ستجد أن أكبر صعوبة في التعليمات البرمجية غير المتزامنة هي بالفعل:
كيفية تنظيم الرابط غير المتزامن بالكامل بحيث لا يصبح أكثر تشتتاً ويصعب تغييره كما هو مكتوب.
في هذه المقالة، لا أريد أن أتحدث عن واجهة برمجة تطبيقات واحدة، ولكن أريد أن أتحدث عن المبادئ التنظيمية التي أقدرها أكثر في المشاريع الحقيقية.
1. ما أشاركه أولاً هو المسؤولية.
عند إفساد التعليمات البرمجية غير المتزامنة، غالبًا ما يكون رد الفعل الأول هو “تفكيك الوظيفة”. من المؤكد أن تقسيم الوظائف مفيد، ولكن إذا كانت المسؤوليات مختلطة معًا بالفعل، فإن تقسيمها عادةً ما يتضمن تقسيم الفوضى إلى عدة ملفات صغيرة.
أنا مهتم أكثر بفصل المسؤوليات أولاً. تنقسم عادة إلى ثلاث طبقات على الأقل:
1. طبقة الصفحة
طبقة الصفحة مسؤولة عن:
- تفعيل نية المستخدم
- عرض حالة الصفحة
- الاستجابة للتغيرات التفاعلية
إنه يعرف “ما يجب تحميله الآن”، لكنه لا ينبغي أن يكون مسؤولاً عن “كيفية تنظيم العملية غير المتزامنة بأكملها”.
2. طبقة الحالة / ViewModel
طبقة الدولة مسؤولة عن:
- ترجمة نية المستخدم إلى مهام
- قرر ما إذا كان ينبغي موازاة المهام أو استبدالها أو إلغاؤها
- إدارة دلالات الصفحة مثل التحميل، المحمل، الفاشل، إلخ.
- تحديد النتائج التي لا تزال مؤهلة لإعادة كتابة الصفحة
إنها نقطة النهاية الحقيقية للعمليات غير المتزامنة.
3. طبقة الخدمة
طبقة الخدمة مسؤولة عن:
- ضبط الواجهة
- قراءة ذاكرة التخزين المؤقت
- الجمع بين مصادر بيانات متعددة
- توفير إمكانيات المجال
لا ينبغي لها أن تعرف كيف تبدو الصفحة، ولا ينبغي لها أن تتسلل إلى دلالات حالة واجهة المستخدم.
يتم إفساد الكثير من التعليمات البرمجية غير المتزامنة لأنه بمجرد خلط هذه الطبقات الثلاث، فإن أي تغيير في المتطلبات سيؤثر على واجهة المستخدم والعملية والحالة ومصدر البيانات في نفس الوقت.
2. أهم مبدأ في طبقة الصفحة: معرفة أقل بالتفاصيل غير المتزامنة
لا أحب أن تقوم الصفحة بالعديد من الخطوات غير المتزامنة من تلقاء نفسها. لأنه بمجرد أن تعرف الصفحة الكثير، فإنها ستبدأ في التعامل مع هذه الأشياء:
- طلب التحكم في التسلسل
- الأخطاء
- تصفية النتائج
- سياسة الإلغاء
- تحميل دلالات التقسيم
عند تغيير متطلب كهذا، غالبًا ما تتأثر واجهة المستخدم والعملية معًا.
لذلك أفضّل أن تعبر الصفحة عن هذه الأشياء فقط:
- “أحتاج إلى التحديث الآن”
- “قام المستخدم بالنقر للمحاولة مرة أخرى”
- “تغيرت شروط التصفية”
وأما ما وراء ذلك:
- قراءة ذاكرة التخزين المؤقت أولاً أم فتح الواجهة أولاً؟
- هل تريد إيقاف المهام القديمة؟
- ما إذا كانت النتيجة قد انتهت
- هل يجب أن يشترك التحميل والتحديث الأول في نفس الرابط؟
ضع أكبر قدر ممكن في إغلاق طبقة الحالة.
3. يجب أن تكون حالة الصفحة واضحة، ولا تعتمد على مجموعة من الأجزاء المتناثرة لتهجئة الدلالات.
ستبدو العديد من الصفحات غير المتزامنة بهذا الشكل في المرحلة اللاحقة:
isLoadingisRefreshinghasErrorshowRetryisEmptyitems
تكون هذه القيم معقولة عند النظر إليها بشكل فردي، ولكنها عرضة للتناقض الذاتي عند دمجها:
- التحميل مع الأخطاء القديمة
- يعرض كلاهما الحالة الفارغة ويحتفظان بالقائمة القديمة
- منعش، ولكن شعار التحميل الأول لا يزال موجودًا
لذلك فإنني أقدر “الحالة الدلالية للصفحة” أكثر من “العديد من الحالات الصغيرة التي يمكن دمجها بحرية”.
لأن المهم حقًا في الصفحة غير المتزامنة هو ما إذا كان يمكنها أن تحدد بوضوح المرحلة التي تمر بها الصفحة حاليًا.
4. يجب أن تكون حدود المهام واضحة، وإلا فسيتم “تشغيل كل شيء أولاً”
يعتمد ما إذا كانت البنية غير المتزامنة مستقرة أم لا على ما إذا كان يمكنها الإجابة على هذه الأسئلة:
- من يملك هذه المهمة؟ -هل يستمر عند ترك الصفحة؟
- عندما تأتي مهمة جديدة، هل تصبح المهمة القديمة غير صالحة على الفور؟
- هل هذه مهمة قائمة بذاتها أم جزء من عملية أطول؟
إذا لم يكن من الممكن الإجابة على هذه الأسئلة بشكل واضح، فإن الكود التالي سيدخل الحالة بالتأكيد:
- يبدو أن كل مكان واضح بذاته
- لكن لا يمكن لأحد أن يجمع رواية كاملة لكيفية عمل هذا الرابط.
إذا كان من الصعب تكرار جزء من التعليمات البرمجية غير المتزامنة باللغة الطبيعية، فعادةً ما يكون من الصعب الحفاظ عليه بشكل ثابت لاحقًا.
5. سأبذل قصارى جهدي لبناء “صلاحية النتيجة” في العملية
لقد افسدت العديد من الصفحات. ظاهريًا، يبدو أن النتيجة قد فشلت، لكنها في الواقع أقرب:
- تم إرجاع النتائج القديمة بنجاح
- لكنه لم يعد يتوافق مع سياق الصفحة الحالية
من المرجح أن يحدث هذا النوع من المشاكل بشكل خاص عندما:
- بحث
- تبديل التصفية
- ترقيم الصفحات
- الوصول بسرعة إلى صفحة الخروج
إذا لم يتم تصميم فعالية النتائج في التصميم، فسوف تقوم الصفحة عاجلاً أم آجلاً بتطوير هذه الظواهر الغريبة:
- التراجع عن المحتوى
- ينتهي التحميل لسبب غير مفهوم
- مطالبات الخطأ بالكتابة فوق حالة النجاح الحالية
لذلك يهمني:
- من سيحكم على انتهاء صلاحية النتيجة؟
- ما إذا كان يجب الحكم على كل نقطة اتصال بنفسك
- استمر في إغلاق الواجهة بشكل موحد في طبقة الحالة
تفضيلاتي واضحة جدًا: **حاول أن تكون متسقًا في الختام، ولا تدع كل فرع من فروع العرض يحكم بنفسه “ما إذا كانت النتيجة هذه المرة لا تزال مهمة”. **
6. لا تتسلل دلالات الصفحة إلى طبقة الخدمة
تم إفساد الكثير من التعليمات البرمجية. ظاهريًا، يبدو أن طبقة الخدمة لن تقدم طلبات. في الواقع، إنها أقرب إلى طبقة الخدمة وتبدأ في دمج مفاهيم الصفحة ببطء.
على سبيل المثال، تبدأ التسمية أو المنطق مثل هذا في الظهور في طبقة الخدمة:
- “طلب خاص للتحميل الأول للصفحة الرئيسية”
- “صفحة التفاصيل فارغة ومليئة بالمنطق”
- “كتابة خطأ لهذه الصفحة”
وبمجرد خلط هذه العناصر، ستصبح إعادة استخدام الهياكل اللاحقة أكثر صعوبة. نظرًا لأن طبقة الخدمة تبدأ في معرفة شكل الصفحة، وتبدأ طبقة الصفحة في معرفة تفاصيل التنفيذ الموجودة في طبقة الخدمة، فإن الحدود تصبح غير واضحة بسرعة.
طبقة الخدمة أكثر ملاءمة للتركيز على:
-ما هي البيانات التي حصلت عليها؟
- كيفية الجمع بين مصادر البيانات
- ما هي استراتيجية التخزين المؤقت؟
بدلاً من “كيف يجب أن تتصرف هذه الصفحة؟”
7. أهتم بمبدأ واحد: يجب قراءة العملية غير المتزامنة بوضوح
هذا معيار شائع الاستخدام عندما أقوم بمراجعة التعليمات البرمجية بنفسي.
إذا التقطت جزءًا من التعليمات البرمجية غير المتزامنة، فمن الصعب بالفعل تكرارها باللغة الطبيعية:
- ما هو المدخل؟
- ما هي العملية الرئيسية؟
- ما هي المهام التي سيتم إلغاؤها؟
- ما هي النتائج التي سيتم تجاهلها
- ما هي الحالات التي تتغير حسب أي طبقة؟
حتى لو كان من الممكن تشغيل هذا الكود اليوم، فمن المرجح أن يصبح تطويره أكثر صعوبة في المستقبل.
الخوف الحقيقي من الكود غير المتزامن هو أنه غير واضح. بمجرد عدم قدرتك على تحديد ذلك بوضوح، فإن أي تكرارات لاحقة ستكون مجرد مسألة حظ.
8. فكرة تنظيمية أقرب إلى التنفيذ
ولو أردت أن ألخص الأمر في جملة عملية أكثر، سأنظمه على النحو التالي:
- طبقة الصفحة: التعبير عن النية
- طبقة الحالة: إغلاق المهام ودلالات الحالة
- طبقة الخدمة: توفير القدرات
- طبقة الموارد المشتركة: استخدم الممثل أو وسائل العزل الأخرى لإدارة الحالة المشتركة عند الضرورة
ثم قم بتوضيح هذه العلاقات في طبقة الحالة:
- ما هي المهام التي يستبعد بعضها بعضا
- ما هي المهام المتوازية
- ما هي النتائج التي يمكن التخلص منها
- ما هي الدول التي يجب تحديثها من قبل الفاعل الرئيسي
الموقف الشائع هو أن هذا “يبدو وكأنه طبقة إضافية”، ولكن في المشاريع المعقدة حقًا، تهدف هذه الطبقات إلى منع المنطق غير المتزامن من الانتشار مباشرة إلى كل حدث صفحة.
9. الاستنتاج: جوهر التنظيم غير المتزامن في المشاريع الحقيقية هو الحدود
أهم شيء في المشروع الحقيقي ليس أبدًا ما إذا كان سيكون Task أو async let أو Actor.
إن ما يحدد حقًا ما إذا كانت التعليمات البرمجية يمكن أن تتطور على المدى الطويل هو ما إذا كانت هذه الحدود واضحة:
- الحدود بين طبقة الصفحة وطبقة العملية
- الحدود بين طبقة الحالة وطبقة الخدمة
- الحدود بين الحالة المشتركة والحالة الطبيعية
- الحد الفاصل بين النتائج الصالحة الحالية والنتائج منتهية الصلاحية
فإذا كان لا بد لي من تلخيص هذا المقال في جملة واحدة، لقلت:
المفتاح لتنظيم التعليمات البرمجية غير المتزامنة في المشاريع الحقيقية ليس “كيفية كتابة واجهة برمجة التطبيقات غير المتزامنة”، ولكن “ما إذا كانت الحدود الأربعة للمهام والحالة والنتائج والمسؤوليات مرسومة بوضوح.”
فقط عندما تكون هذه الحدود واضحة، يمكن أن تتغير التعليمات البرمجية غير المتزامنة حقًا من “يمكن تشغيلها” إلى “يمكن صيانتها لفترة طويلة”.
What to read next
Want more posts about Swift Concurrency?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to keep following #Architecture?
Tags are useful for related tools, specific problems, and similar troubleshooting notes.
View same tagWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home