جافا سكريبت والسادة: لماذا تحتاج إلى أنماط التصميم (وكيف ستُبسّط حياتك)
\n\nهل سبق لك أن فتحت مشروعك القديم على JavaScript وفكرت: «من كتب هذا؟ هذه معكرونة كود!»؟ إذا كان الأمر كذلك — مرحبًا بك في النادي. عاجلاً أم آجلاً، يواجه كل مطور موقفًا يتحول فيه الكود إلى فوضى. وهنا تأتي أنماط التصميم في JavaScript للإنقاذ — وهي وصفات مجربة للمهام النمطية.
\n\nلكن دعنا نتفق فورًا: الأنماط ليست رصاصة فضية. إنها ليست سحرًا يجعل كودك مثاليًا بين ليلة وضحاها. إنها أدوات. وأي أداة تكون جيدة فقط عندما تعرف متى ولماذا تستخدمها. هذا بالضبط ما سنتحدث عنه.
\n\nما هي أنماط التصميم بكلمات بسيطة؟
\n\nتخيل أنك مهندس معماري. أنت لا تخترع شكلاً جديدًا للعجلة أو المدخل في كل مرة. أنت تستخدم حلولاً مجربة: الأقواس، الأعمدة، الجدران الحاملة. أنماط التصميم في JS هي نفس «المخططات المعمارية»، ولكن للكود.
\n\nإنها تحل ثلاث مشاكل رئيسية:
\n- \n
- التكرار — لا حاجة لإعادة اختراع العجلة؛ \n
- قابلية القراءة — مطور آخر سيفهم منطقك فورًا؛ \n
- قابلية التوسع — يمكن توسيع الكود بسهولة دون خطر كسر كل شيء. \n
في هذه المقالة، سنستعرض أربعة من أشهر الأنماط التي ستفيدك حقًا في العمل اليومي. لا ماء — فقط كود وممارسة.
\n\n1. Singleton (الوحيد): عندما تحتاج إلى نسخة واحدة فقط
\n\nهناك حالات يجب أن يكون فيها نسخة واحدة بالضبط من الفئة في التطبيق. على سبيل المثال:
\n- \n
- مخزن الحالة العام (مثل Redux store)؛ \n
- مسجل لتسجيل الأخطاء؛ \n
- اتصال بقاعدة بيانات أو API. \n
يضمن نمط Singleton أنه بغض النظر عن عدد المرات التي تحاول فيها إنشاء كائن — ستحصل على نفس الكائن.
\n\nمثال: عداد الزيارات
\n\nclass Counter {\n constructor() {\n if (Counter.instance) {\n return Counter.instance;\n }\n this.count = 0;\n Counter.instance = this;\n }\n\n increment() {\n this.count++;\n }\n\n getCount() {\n return this.count;\n }\n}\n\n// الاستخدام\nconst counter1 = new Counter();\nconst counter2 = new Counter();\n\ncounter1.increment();\ncounter1.increment();\n\nconsole.log(counter2.getCount()); // 2 — لأنه نفس الكائن\nconsole.log(counter1 === counter2); // true\n\n\nمتى تستخدم: عندما يكون من الضروري أن يكون لديك نقطة وصول واحدة للمورد. لكن كن حذرًا — أحد الآثار الجانبية الشائعة لـ Singleton في JS هو الحالة العامة التي يصعب اختبارها. لا تفرط في استخدامه.
\n\n2. Module (الوحدة النمطية): التغليف بذكاء
\n\nقبل ظهور ES6 Modules، استخدم المطورون نمط Module لإنشاء دوال خاصة وعامة. حتى الآن، مع وجود import/export، يظل هذا النهج مفيدًا لتنظيم المنطق داخل ملف واحد.
\n\nالسر يكمن في الإغلاقات (closures). ننشئ دالة تُرجع كائنًا بالدوال، بينما تبقى المتغيرات الداخلية غير قابلة للوصول من الخارج.
\n\nمثال: سلة التسوق لمتجر إلكتروني
\n\nconst ShoppingCart = (function() {\n // متغيرات خاصة\n let items = [];\n \n // دالة خاصة\n function calculateTotal() {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n }\n\n // واجهة عامة\n return {\n addItem(product) {\n const existing = items.find(item => item.id === product.id);\n if (existing) {\n existing.quantity++;\n } else {\n items.push({ ...product, quantity: 1 });\n }\n },\n removeItem(productId) {\n items = items.filter(item => item.id !== productId);\n },\n getTotal() {\n return calculateTotal();\n },\n clear() {\n items = [];\n }\n };\n}