HeadlinesBriefing favicon HeadlinesBriefing.com

بنية التصميم النواة المحددة الشيل غير المحدد

Hacker News •
×

منذ أربعة عشر عاماً، اخترع غاري برنهارت مصطلح Functional Core, Imperative Shell. وكأن معظم الأفكار الجيدة في الكمبيوتر، لم يكن جديداً تماماً، لكن فكرته كانت واضحة جداً، وتشكل أساساً ممتازاً للحديث عن الاختبار والتحديد في الأنظمة القائمة. باختصار، تقسم بنية Functional Core Imperative Shell الكود إلى جزأين. يكون Functional Core نيّفاً دالياً - أي أنه لا يحتوي على عمليات I/O ولا يحدث تحديثات للحالة المتدمرة. يهتم بالمنطق الأعمال للتطبيق. يملك Imperative Shell مسارات أقل، لكنه يحتفظ بالحالة، ويتنسيق التعليمات الخارجية، ويتعامل مع العالم الخارجي - أي مع عمليات I/O. مهمته هي استعلام النواة بقيم، وتلقي قيم أخرى كنتيجة لقرارة blackbox، واستخدامها للتفاعل مع العالم الخارجي؛ سواء كان ذلك كتابة إلى قاعدة بيانات، أو إرسال طلب، أو تحديث واجهة المستخدم الرسومية. لدي نواة وشيل خصائص متميزة في هذا النموذج: النواة الشيل يتخذ القرارات يتنسيق التعليمات الخارجية الكثير من مسارات التنفيذ المتفرعة أكثر تنفيذاً خطياً معزول عن العالم يدمج مع العالم هذا يجعل النواة ملائمة للاختبار بشدة. نظراً أنها نيّفاً دالياً، فإن نفس المدخلات ستحصل دائماً على نفس النتائج. نظراً أنها معزولة، لا شيء يمكن توفيه أو استبداله. وبما أنها تتعامل مع المنطق الأعمال المعقد، يمكن للاختبارات أن تخبرنا الكثير عن سلوك النظام. النقاء الوظيفي والتحديد طريقة أقصر لوصف الخصائص التي تجعل الدوال النية مناسبة للاختبار هي أنها محددة. أي أنه، بافتراض تدفق من المدخلات، فإن الدالة النية دائماً تعيد تدفقاً من المخرجات نفسه؛ سلوكها قابل للتكرار. لكن البرمجة الوظيفية النية ليست الطريق الوحيد للوصول إلى ذلك. إذا منحنا رأسنا بعض الاهتمام، يمكننا أن نرى أن تدفق القيم ومتسلسلة المعاملات هما طرق مختلفة للتعبير عن الشيء نفسه، ويمكن لآلات الحالة أن تجلب لنا نفس المواصفات. دعنا نأخذ الكود التالي في الاعتبار: function add(ns) {return ns.reduce((a, b) => a + b, 0)} class Add Machine {#state = 0 transition(input) {this.#state += input} get state() {return this.#state}} الدالة add سهلة الفهم؛ هي نية وبالتالي محددة. لكن الـ Add Machine محددة أيضاً - بافتراض نفس تسلسل من المكالمات إلى دالة transition، سيعيد الـ Add Machine نفس الحالة. لا يغير ذلك أنه يكون أمراً أمراً. const output = add([1, 2, 3]) const a = new Add Machine() a.transition(1) a.transition(2) a.transition(3) const output = a.state البرمجة الوظيفية النية هي واحدة من الأنماط الجيدة، لكن بسبب اعتبارات اللغة أو الأداء، ليست دائماً عملية - لا أريد محاولة ذلك في C! لكن بخفض المتطلبات من الوظيفة النية إلى مجرد محدد، نحتفظ بفوائد قابلية الاختبار من Functional Core Imperative Shell، بينما نوسع نطاق تطبيقه. وهكذا، عنوان هذه المنشور هو: النواة المحددة، الشيل غير المحدد. قد يشعر التحديد بمفهوم أكثر تعقيداً من النقاء الوظيفي. كيف تعرفه عندما تراه؟ أجد أنه أسهل أن نبدأ بما هو غير محدد والعمل إلى الخلف. إليك بعض الأمثلة الشائعة على سلوك غير قابل للتكرار: استدعاء مولدات الأعداد العشوائية التي لم يتم تهيئتها Asynchronous والعمليات متعددة الخيوط تفاعل على الشبكة تفاعل مع عمليات أخرى قراءة كتابة إلى التخزين المحلي تفاعلات قاعدة البيانات طلب من نظام التشغيل التاريخ أو الوقت جميع هذه تندرج ضمن الشيل غير المحدد. عندما تجدها في منطقك الأعمال، لديك هدف طبيعي للتجزئة - إما تقسيم الدالة إلى جزأين حولها، أو رفعها إلى طبقة أعلى وإدخال نتيجتها كمعلمة. من المثير للاهتمام التفكير في ميتافورة الشيل بشكل حرفي؛ يجب أن يحيط بالمنطق، واستعلام القلب من التطبيق للحصول على ما يحتاجه. العمل مع ما لديك هذا كل شيء جيد على الرغم من ذلك، ربما تفكر، لكن ما الفائدة منه بالنسبة لي، العامل الرجلعي في الكود؟