أنماط التصميم
مقدمة
لماذا يكون كودك دائمًا "يعمل لكنه فوضوي"؟ ربما واجهت هذا الموقف: عندما تتغير المتطلبات، يجب تعديل الكود في أماكن كثيرة؛ تريد إعادة استخدام منطق معين، لكنك تجده متشابكًا مع كود آخر. أنماط التصميم هي "وصفات تنظيم الكود" التي لخصها السابقون، وتساعدك على كتابة كود مرن وقابل للصيانة.
سيساعدك هذا الفصل على فهم أنماط التصميم الأكثر عملية، ليس للحفظ، بل لفهم "أي نمط يُستخدم في أي موقف".
ماذا ستتعلم في هذه المقالة؟
| الفصل | المحتوى | المفهوم الأساسي |
|---|---|---|
| الفصل 1 | ما هي أنماط التصميم | جوهر الأنماط وتصنيفها |
| الفصل 2 | الأنماط الإنشائية | كيفية إنشاء الكائنات بأناقة |
| الفصل 3 | الأنماط الهيكلية | كيفية تنظيم هيكل الكود |
| الفصل 4 | الأنماط السلوكية | كيفية إدارة التفاعل بين الكائنات |
بعد إكمال هذا الفصل، ستتقن أنماط التصميم الأكثر استخدامًا، وستتمكن من تحديد السيناريوهات المناسبة وتطبيقها بمرونة في المشاريع الفعلية.
0. نظرة عامة: جوهر أنماط التصميم
تخيل أنك تتعلم الطبخ. يمكنك البدء من الصفر في كل مرة، أو يمكنك تعلم الوصفات الكلاسيكية — الوصفات لا تقيّد إبداعك، بل تجعلك تقف على أكتاف من سبقوك. أنماط التصميم هي "الوصفات الكلاسيكية" لعالم البرمجة.
قيمة أنماط التصميم
- لغة مشتركة: قل "هنا نستخدم نمط المراقب" والفريق يفهم نية تصميمك فورًا
- إعادة استخدام الخبرة: لا داعي لارتكاب نفس الأخطاء التي ارتكبها الآخرون
- مرونة التوسع: النمط الجيد يجعل الكود يحتاج تعديلات طفيفة عند التغيير بدلاً من إعادة كتابة كبيرة
من خلال المكون التفاعلي التالي، استعرض تصنيف واستخدامات أنماط التصميم الشائعة:
1. الأنماط الإنشائية: كيفية إنشاء الكائنات بأناقة
1.1 نمط Singleton (المفرد)
السيناريو: يلزم وجود نسخة واحدة فقط عالميًا، مثل مدير الإعدادات، أو مسجل السجلات، أو تجمع اتصالات قاعدة البيانات.
class ConfigManager {
static instance = null
static getInstance() {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager()
}
return ConfigManager.instance
}
constructor() {
this.config = {}
}
}
// بغض النظر عن عدد الاستدعاءات، تكون دائمًا نفس النسخة
const a = ConfigManager.getInstance()
const b = ConfigManager.getInstance()
console.log(a === b) // true1.2 نمط Factory (المصنع)
السيناريو: إنشاء أنواع مختلفة من الكائنات بناءً على شروط مختلفة، دون أن يحتاج المستدعي لمعرفة تفاصيل الإنشاء.
function createNotification(type, message) {
switch (type) {
case 'email':
return { send: () => console.log(`إرسال بريد إلكتروني: ${message}`) }
case 'sms':
return { send: () => console.log(`إرسال رسالة نصية: ${message}`) }
case 'push':
return { send: () => console.log(`إرسال إشعار: ${message}`) }
default:
throw new Error(`نوع إشعار غير معروف: ${type}`)
}
}
// المستدعي لا يهتم بالتنفيذ المحدد
const notification = createNotification('email', 'مرحبًا')
notification.send()2. الأنماط الهيكلية: كيفية تنظيم هيكل الكود
2.1 نمط Adapter (المحوّل)
السيناريو: واجهتان غير متوافقتين تحتاجان "محوّلاً". مثلاً، تنسيق البيانات الذي تعيده واجهة برمجية قديمة لا يتطابق مع التنسيق الذي يتوقعه المكون الجديد.
// التنسيق الذي تعيده واجهة برمجية قديمة
const oldApi = {
getUserInfo: () => ({ user_name: 'أحمد', user_age: 25 })
}
// المحوّل: التحويل إلى التنسيق الجديد
function adaptUser(oldUser) {
return { name: oldUser.user_name, age: oldUser.user_age }
}
const user = adaptUser(oldApi.getUserInfo())
// { name: 'أحمد', age: 25 }2.2 نمط Decorator (المزخرف)
السيناريو: إضافة وظائف جديدة إلى كائن دون تعديل الكود الأصلي. مثل وضع غطاء للهاتف — وظائف الهاتف لا تتغير، لكنه يكتسب الحماية.
// دالة السجل الأساسية
function log(message) {
console.log(message)
}
// تزيين: إضافة طابع زمني
function withTimestamp(fn) {
return (message) => fn(`[${new Date().toISOString()}] ${message}`)
}
// تزيين: إضافة مستوى السجل
function withLevel(fn, level) {
return (message) => fn(`[${level}] ${message}`)
}
const enhancedLog = withTimestamp(withLevel(log, 'INFO'))
enhancedLog('تم تشغيل الخدمة بنجاح')
// [2025-01-15T10:30:00.000Z] [INFO] تم تشغيل الخدمة بنجاح3. الأنماط السلوكية: كيفية إدارة التفاعل بين الكائنات
3.1 نمط Observer (المراقب)
السيناريو: عندما تتغير حالة كائن ما، يجب إخطار الكائنات الأخرى تلقائيًا. مثلاً، عندما يقدم مستخدم طلبًا، يجب إرسال بريد إلكتروني وخصم المخزون وتسجيل السجل في نفس الوقت.
class EventEmitter {
constructor() {
this.listeners = {}
}
on(event, callback) {
if (!this.listeners[event]) this.listeners[event] = []
this.listeners[event].push(callback)
}
emit(event, data) {
(this.listeners[event] || []).forEach(cb => cb(data))
}
}
const bus = new EventEmitter()
bus.on('order:created', (order) => console.log('إرسال بريد تأكيد', order.id))
bus.on('order:created', (order) => console.log('خصم المخزون', order.id))
bus.emit('order:created', { id: 'ORD-001' })3.2 نمط Strategy (الاستراتيجية)
السيناريو: نفس العملية لها خوارزميات/استراتيجيات متعددة يجب التبديل بينها في وقت التشغيل. مثلاً، طرق فرز مختلفة، أو قواعد حساب أسعار مختلفة.
const pricingStrategies = {
normal: (price) => price,
vip: (price) => price * 0.8,
svip: (price) => price * 0.6
}
function calculatePrice(price, memberLevel) {
const strategy = pricingStrategies[memberLevel] || pricingStrategies.normal
return strategy(price)
}
calculatePrice(100, 'vip') // 80
calculatePrice(100, 'svip') // 60من خلال المكون التفاعلي التالي، جرّب تأثيرات أنماط التصميم المختلفة عمليًا:
4. كيف تختار نمط التصميم؟
| المشكلة التي تواجهها | النمط الموصى به | الفكرة الأساسية |
|---|---|---|
| نسخة واحدة فقط عالميًا | Singleton | التحكم في عدد النسخ |
| إنشاء كائنات مختلفة حسب الشروط | Factory | تغليف منطق الإنشاء |
| واجهات غير متوافقة تحتاج تحويلاً | Adapter | تغليف بطاقة تحويل |
| إضافة وظائف ديناميكيًا | Decorator | تغليف طبقات متتالية |
| تغيرات الحالة تُعلم أطرافًا متعددة | Observer | فصل النشر-الاشتراك |
| خوارزميات متعددة قابلة للتبديل | Strategy | تغليف الخوارزميات ككائنات |
المبدأ الأساسي
أنماط التصميم ليست كلما كثرت كان أفضل. التصميم المفرط سيئ بقدر انعدام التصميم. استخدم الأنماط فقط حيث تحتاج مرونة حقيقية، واستخدم حلولًا بسيطة للمشاكل البسيطة. تذكر مبدأ KISS: اجعله بسيطًا يا غبي.
5. دعم الذكاء الاصطناعي: تعلم وتطبيق أنماط التصميم بالنماذج اللغوية الكبيرة
يمكن للنماذج اللغوية الكبيرة مساعدتك في تحديد السيناريوهات في كودك حيث يمكن تطبيق أنماط التصميم، واقتراح خطط إعادة بناء ملموسة.
5.1 تحديد الأنماط القابلة للتطبيق
Prompt:
حلل الكود التالي وحدد ما إذا كانت هناك أماكن يمكن تحسينها بأنماط التصميم. إذا وُجدت، يرجى توضيح: 1. مشكلة الكود الحالية 2. نمط التصميم الموصى به 3. مثال على الكود بعد إعادة البناء 4. لماذا هذا النمط مناسب لهذا السيناريو [الصق الكود الخاص بك]
5.2 تعلم الأنماط بسيناريوهات ملموسة
Prompt:
باستخدام سيناريو حقيقي "نظام طلب توصيل الطعام"، اعرض تطبيق أنماط التصميم التالية: - نمط Factory: إنشاء أنواع مختلفة من الطلبات - نمط Observer: إشعار تغير حالة الطلب - نمط Strategy: قواعد حساب رسوم التوصيل المختلفة استخدم أمثلة كود JavaScript، اعرض أولاً المشكلة دون النمط ثم التحسن بعد تطبيق النمط.
5.3 تقييم是否存在 تصميم مفرط
Prompt:
راجع الكود التالي وحدد ما إذا كانت هناك مشاكل تصميم مفرط. هل هناك تجريدات غير ضرورية، أو أنماط تصميم غير مستخدمة، أو تحسينات مبكرة؟ إذا وُجدت، اقترح كيفية التبسيط وفقًا لمبدأ KISS. [الصق الكود الخاص بك]
نصائح استخدام الذكاء الاصطناعي
اطلب من الذكاء الاصطناعي شرح أنماط التصميم باستخدام سيناريوهات عمل مألوفة لك — فهذا أكثر فعالية بكثير من النظر إلى مخططات UML المجردة. لكن تذكر: قد يميل الذكاء الاصطناعي إلى اقتراح حلول أكثر تعقيدًا، وتحتاج للحكم بنفسك على ما إذا كنت تحتاجها فعلًا.
6. الخلاصة
- الأنماط الإنشائية: تحل مشكلة "كيفية إنشاء الكائنات"، مما يجعل عملية الإنشاء أكثر مرونة
- الأنماط الهيكلية: تحل مشكلة "كيفية تنظيم الكود"، مما يجعل الهيكل أكثر وضوحًا
- الأنماط السلوكية: تحل مشكلة "كيفية تفاعل الكائنات"، مما يجعل التعاون أكثر ارتباكًا فضفاضًا
- التطبيق المرن: اختر حسب السيناريو الفعلي، لا تستخدم الأنماط لمجرد استخدامها
تأمل أخير
جوهر أنماط التصميم هو إدارة التغيير. التصميم الجيد يجعل الأجزاء المتغيرة سهلة التعديل، والأجزاء الثابتة مستقرة. عندما تكتب كودًا، اسأل نفسك: "إذا تغيرت المتطلبات، كم مكانًا أحتاج لتعديله؟" — إذا كانت الإجابة "أماكن كثيرة"، فقد تحتاج نمط تصميم للمساعدة.
قراءات إضافية
- كتاب كلاسيكي: Design Patterns: Elements of Reusable Object-Oriented Software لعصابة الأربعة (GoF) هو العمل التأسيسي لأنماط التصميم.
- منظور حديث: في JavaScript، أصبحت العديد من الأنماط أكثر إيجازًا بفضل خصائص اللغة (ال closures، الدوال الأعلى رتبة).
- نصائح عملية: افهم المشكلة أولاً، ثم فكر في النمط. لا تحمل مطرقة وتبحث عن مسمار.
- تعلم متقدم: تعرف على مبادئ SOLID، فهي الفلسفة الكامنة وراء أنماط التصميم.