مرحبا بالجميع! لا يخفى على أحد أنه في عالم البرمجة هناك العديد من التقنيات والممارسات وأنماط البرمجة (التصميم) ، ولكن في كثير من الأحيان ، تعلم شيء جديد ، ليس من الواضح تمامًا أين وكيف يتم تطبيق هذا الجديد.
اليوم ، باستخدام مثال إنشاء وحدة غلاف صغيرة للعمل مع طلبات http ، سنقوم بتحليل الفوائد الحقيقية للكاري - استقبال البرمجة الوظيفية.
إلى جميع القادمين الجدد والمهتمين باستخدام البرمجة الوظيفية في الممارسة - مرحبًا ، أولئك الذين يفهمون تمامًا ما هو الكاري - أتطلع إلى تعليقاتك على الرمز ، لأنه كما يقولون - لا يوجد حد للكمال.
لذلك دعونا نبدأ
ولكن ليس من مفهوم الكاري ، ولكن من بيان المشكلة ، حيث يمكننا تطبيقه.
لدينا واجهة برمجة تطبيقات خاصة بالمدونة تعمل وفقًا للمبدأ التالي (جميع المطابقات مع واجهات برمجة التطبيقات الحقيقية هي حادث):
- طلب
/api/v1/index/
سيعيد بيانات الصفحة الرئيسية - طلب
/api/v1/news/
سيعيد البيانات لصفحة الأخبار - طلب
/api/v1/articles/
سيعيد البيانات لقائمة المقالات - request to
/api/v1/article/222/
سيعرض صفحة المقالة ذات المعرف 222 - طلب
/api/v1/article/edit/222/
سيعيد نموذج تحرير مقال بمعرف 222
وهكذا دواليك
كما ترى ، من أجل الوصول إلى واجهة برمجة التطبيقات ، نحتاج إلى الانتقال إلى واجهة برمجة التطبيقات الخاصة بإصدار معين v1 (كم سيزداد حجمه وسيتم إصدار إصدار جديد) ثم نقوم بتصميم طلب البيانات بشكل أكبر.
لذلك ، في كود js ، للحصول على البيانات ، على سبيل المثال ، مقالة واحدة بمعرف 222 ، يجب أن نكتب (لتبسيط المثال قدر الإمكان ، نستخدم طريقة جلب js الأصلية):
fetch('/api/v1/article/222/') .then() .catch()
لتعديل نفس المقالة ، سنطلب ما يلي:
fetch('/api/v1/article/edit/222/') .then() .catch()
بالتأكيد لاحظت بالفعل أنه في طلباتنا هناك الكثير من المسارات المكررة. على سبيل المثال ، المسار والنسخة إلى API /api/v1/
، والعمل مع مقال واحد /api/v1/article/
و /api/v1/article/edit/
.
بعد قاعدة DRY المفضلة (لا تكرر نفسك) ، كيف يمكن تحسين رمز طلب واجهة برمجة التطبيقات؟
يمكننا إضافة أجزاء الاستعلام إلى الثوابت ، على سبيل المثال:
const API = '/api' const VERSION = '/v1' const ARTICLE = `${API}${VERSION}/article`
والآن يمكننا إعادة كتابة الأمثلة أعلاه بهذه الطريقة:
طلب المادة
fetch(`${ARTICLE}/222/`)
طلب تحرير مادة
fetch(`${ARTICLE}/edit/222/`)
يبدو أن الشفرة أقل ، فهناك ثوابت مرتبطة بواجهة برمجة التطبيقات ، ولكن أنا وأنت تعرف ما يمكن القيام به بشكل أكثر ملاءمة.
أعتقد أنه لا تزال هناك خيارات لحل المشكلة ، لكن مهمتنا هي النظر في الحل باستخدام الكاري.
مبدأ بناء الطلبات على أساس خدمات المتشعب
تتمثل الإستراتيجية في إنشاء وظيفة معينة ، من خلال الاتصال الذي سنقوم ببناء طلبات API.
كيف يجب أن تعمل
نقوم ببناء الطلب عن طريق استدعاء دالة اللف على الجلب الأصلي (دعنا نسميها http. أدناه هو الرمز الكامل لهذه الوظيفة) ، في الوسيطات التي نمرر فيها معلمات الطلب:
cosnt httpToArticleId222 = http({ url: '/api/v1/article/222/', method: 'POST' })
يرجى ملاحظة أن نتيجة هذه الدالة http ستكون وظيفة تحتوي على إعدادات عنوان url وطلب الطريقة.
الآن ، عن طريق استدعاء httpToArticleId222()
نرسل الطلب بالفعل إلى API.
يمكنك إجراء استعلامات تصميم أكثر تعقيدًا وتدريجًا. وبالتالي ، يمكننا إنشاء مجموعة من الوظائف الجاهزة بمسارات API السلكية. سنطلق عليهم خدمات http.
لذا ، أولاً ، نقوم بإنشاء خدمة استدعاء API (في نفس الوقت نضيف معلمات الطلب التي لم تتغير لكل الطلبات اللاحقة ، على سبيل المثال ، طريقة)
const httpAPI = http({ url: '/api', method: 'POST' })
نقوم الآن بإنشاء خدمة الوصول إلى واجهة برمجة التطبيقات الخاصة بالإصدار الأول. في المستقبل ، سنكون قادرين على إنشاء فرع طلب منفصل من خدمة httpAPI إلى إصدار مختلف من API.
const httpAPIv1 = httpAPI({ url: '/v1' })
خدمة الوصول إلى واجهة برمجة التطبيقات الخاصة بالإصدار الأول جاهزة. الآن سننشئ خدمات لبقية البيانات منه (تذكر القائمة المرتجلة في بداية المقالة)
بيانات الصفحة الرئيسية
const httpAPIv1Main = httpAPIv1({ url: '/index' })
بيانات صفحة الأخبار
const httpAPIv1News = httpAPIv1({ url: '/news' })
بيانات قائمة المقالات
const httpAPIv1Articles = httpAPIv1({ url: '/articles' })
أخيرًا ، نأتي إلى مثالنا الرئيسي ، بيانات المادة
const httpAPIv1Article = httpAPIv1({ url: '/article' })
كيف تحصل على مسار لتحرير المقالة؟ بالطبع ، لقد خمنت ذلك ، نحن بصدد تحميل البيانات من الوظيفة httpAPIv1Article التي تم إنشاؤها مسبقًا
const httpAPIv1ArticleEdit = httpAPIv1({ url: '/edit' })
نتيجة منطقية صغيرة
لذا ، لدينا قائمة جميلة من الخدمات ، على سبيل المثال ، موجودة في ملف منفصل ، وهذا لا يزعجنا على الإطلاق. إذا كان هناك شيء يجب تغييره في الطلب ، فأنا أعرف بالضبط مكان التعديل.
export { httpAPIv1Main, httpAPIv1News, httpAPIv1Articles, httpAPIv1Article, httpAPIv1ArticleEdit }
أنا أستورد خدمة بوظيفة محددة
import { httpAPIv1Article } from 'services'
وأقوم بتنفيذ الطلب ، وأعيد بناءه أولاً عن طريق إضافة معرف المادة ، ثم أستدعي الوظيفة لإرسال الطلب (كما يقولون: "سهل")
httpAPIv1Article({ url: ArticleID // id - })() .then() .catch()
نظيفة وجميلة ومفهومة (وليس الإعلان)
كيف يعمل
يمكننا "تحميل" دالة بالبيانات على وجه التحديد بسبب التنظيف.
القليل من النظرية.
يُعد Currying طريقة لإنشاء دالة لها القدرة على تطبيق حججها تدريجيًا. يتم تحقيق ذلك عن طريق إرجاع الوظيفة بعد استدعائها.
مثال كلاسيكي هو الإضافة.
لدينا دالة المجموع ، في المرة الأولى التي نسميها ، نقوم بتمرير الرقم الأول للطي اللاحق. بعد الاتصال به ، نحصل على دالة جديدة تتوقع رقمًا ثانيًا لحساب المجموع. إليك رمزها (بنية ES6)
const sum = a => b => a + b
نسميها المرة الأولى (تطبيق جزئي) وحفظ النتيجة في متغير ، على سبيل المثال ، sum13
const sum13 = sum(13)
الآن sum13 يمكننا أيضًا الاتصال بالرقم المفقود في الوسيطة ، وستكون النتيجة 13 + الوسيطة الثانية
sum13(7)
حسنًا ، كيف نطبق هذا على مهمتنا؟
نقوم بإنشاء دالة http ، والتي ستكون الغلاف على الجلب
function http (paramUser) {}
حيث paramUser هي معلمات طلب يتم تمريرها في وقت استدعاء الوظيفة
دعونا نبدأ بإضافة منطق إلى وظيفتنا.
إضافة معلمات الطلب التي تم تعيينها بشكل افتراضي.
function http (paramUser) { /** * -, * @type {string} */ let param = { method: 'GET', credentials: 'same-origin' } }
ثم وظيفة paramGen التي تولد معلمات الطلب من تلك التي تم تعيينها افتراضيًا ومعرفة من قبل المستخدم (في الواقع ، مجرد دمج بين كائنين)
function http (paramUser) { /** * -, * @type {string} */ let param = { method: 'GET', credentials: 'same-origin' } /** * , * url , * * @param {object} param * @param {object} paramUser , * * @return {object} */ function paramGen (param, paramUser) { let url = param.url || '' let newParam = Object.assign({}, param, paramUser) url += paramUser.url || '' newParam.url = url return newParam } }
ننتقل إلى الأهم ، نصف الكاري
إن الوظيفة التي تسمى ، على سبيل المثال ، النسيج والتي يتم إرجاعها بواسطة الدالة http ستساعدنا في ذلك.
function http (paramUser) { /** * -, * @type {string} */ let param = { method: 'GET', credentials: 'same-origin' } /** * , * url , * * @param {object} param * @param {object} paramUser , * * @return {object} */ function paramGen (param, paramUser) { let url = param.url || '' url += paramUser.url || '' let newParam = Object.assign({}, param, paramUser); newParam.url = url return newParam } /** * , * , * * : * * - , , * - , * - , * * @param {object} param , * @param {object} paramUser , * * @return {function || promise} , (fetch), */ function fabric (param, paramUser) { if (paramUser) { if (typeof paramUser === 'string') { return fabric.bind(null, paramGen(param, { url: paramUser })) } return fabric.bind(null, paramGen(param, paramUser)) } else { // , , param url, // :) return fetch(param.url, param) } } return fabric.bind(null, paramGen(param, paramUser)) }
تقوم الاستدعاء الأول للدالة http بإرجاع دالة النسيج ، مع تمرير المعلمات المعلمية إليها (وتكوينها بواسطة الدالة paramGen ) ، والتي ستنتظر ساعات الاتصال لاحقًا.
على سبيل المثال ، قم بتكوين الطلب
let httpGift = http({ url: '
وباستدعاء httpGift ، يتم تطبيق المعلمات التي تم تمريرها ، ونتيجة لذلك نعيد الجلب ، إذا أردنا إعادة تكوين الطلب ، فإننا ببساطة نمرر المعلمات الجديدة إلى وظيفة httpGift التي تم إنشاؤها وننتظر أن يتم استدعاؤها بدون وسيطات
httpGift() .then() .catch()
الملخص
بفضل استخدام الكاري في تطوير وحدات مختلفة ، يمكننا تحقيق مرونة عالية في استخدام الوحدات وسهولة الاختبار. على سبيل المثال ، عند تنظيم بنية الخدمات للعمل مع API.
يبدو الأمر كما لو أننا نقوم بإنشاء مكتبة مصغرة ، باستخدام الأدوات التي نقوم بإنشاء بنية تحتية واحدة لتطبيقنا.
آمل أن تكون المعلومات مفيدة ، لا تضغط بشدة ، هذه مقالتي الأولى في حياتي :)
جميع التعليمات البرمجية المترجمة ، أراك قريبا!