JavaScript المتقدم (ES6+)

أنماط التصميم (Design Patterns)

13 دقيقة الدرس 34 من 40

أنماط التصميم (Design Patterns)

مرحباً بك في أنماط التصميم! في هذا الدرس، سنستكشف حلولاً مثبتة للمشاكل البرمجية الشائعة. تساعدك أنماط التصميم في كتابة كود أنظف وأكثر قابلية للصيانة والتوسع من خلال تطبيق مناهج معمارية مختبرة.

ما هي أنماط التصميم؟

أنماط التصميم هي حلول قابلة لإعادة الاستخدام لمشاكل تحدث بشكل شائع في تصميم البرمجيات. تمثل أفضل الممارسات المحسّنة بمرور الوقت من قبل المطورين ذوي الخبرة:

الفوائد الرئيسية: تحسّن أنماط التصميم قابلية قراءة الكود، وتوفر مفردات مشتركة للمطورين، وتمنع الأخطاء الشائعة، وتجعل الكود أسهل للصيانة والتوسع.

فئات الأنماط

تُجمع أنماط التصميم عادةً في ثلاث فئات رئيسية:

1. أنماط الإنشاء (Creational Patterns) - تتعامل مع آليات إنشاء الكائنات - Singleton، Factory، Builder 2. أنماط البنية (Structural Patterns) - تتعامل مع تركيب الكائنات - Module، Decorator، Facade 3. أنماط السلوك (Behavioral Patterns) - تتعامل مع التواصل بين الكائنات - Observer، Strategy، Command

نمط Singleton

يضمن أن الفئة لها مثيل واحد فقط ويوفر نقطة وصول عامة إليه:

تطبيق Singleton: class Database { constructor() { if (Database.instance) { return Database.instance; } this.connection = null; Database.instance = this; } connect(url) { if (!this.connection) { this.connection = `Connected to ${url}`; console.log(this.connection); } return this.connection; } disconnect() { this.connection = null; console.log('Disconnected'); } } // الاستخدام const db1 = new Database(); db1.connect('mongodb://localhost'); const db2 = new Database(); db2.connect('mongodb://production'); // لن ينشئ اتصالاً جديداً console.log(db1 === db2); // true - نفس المثيل
Singleton حديث مع الوحدات: // database.js class Database { constructor() { this.connection = null; } connect(url) { if (!this.connection) { this.connection = `Connected to ${url}`; } return this.connection; } } // تصدير مثيل واحد export default new Database(); // app.js import db from './database.js'; db.connect('mongodb://localhost');
حالة الاستخدام: كائنات التكوين، خدمات التسجيل، اتصالات قاعدة البيانات، مديري الذاكرة المؤقتة - أي شيء يجب أن يكون له مثيل واحد فقط في التطبيق.

نمط Factory

يوفر واجهة لإنشاء الكائنات دون تحديد فئتها الدقيقة:

تطبيق نمط Factory: class Car { constructor(options) { this.doors = options.doors || 4; this.color = options.color || 'silver'; this.type = 'car'; } } class Truck { constructor(options) { this.doors = options.doors || 2; this.color = options.color || 'white'; this.wheelSize = options.wheelSize || 'large'; this.type = 'truck'; } } class Motorcycle { constructor(options) { this.color = options.color || 'black'; this.engineSize = options.engineSize || '500cc'; this.type = 'motorcycle'; } } // المصنع class VehicleFactory { createVehicle(type, options) { switch(type) { case 'car': return new Car(options); case 'truck': return new Truck(options); case 'motorcycle': return new Motorcycle(options); default: throw new Error(`Vehicle type ${type} not recognized`); } } } // الاستخدام const factory = new VehicleFactory(); const car = factory.createVehicle('car', { color: 'red', doors: 2 }); const truck = factory.createVehicle('truck', { color: 'blue' }); const bike = factory.createVehicle('motorcycle', { engineSize: '1000cc' }); console.log(car); // Car { doors: 2, color: 'red', type: 'car' } console.log(truck); // Truck { doors: 2, color: 'blue', wheelSize: 'large', type: 'truck' }

نمط Module

يوفر التغليف والأعضاء الخاصة باستخدام closures:

نمط Module: const CounterModule = (function() { // متغيرات خاصة let count = 0; const maxCount = 100; // دالة خاصة function logCount() { console.log(`Current count: ${count}`); } // واجهة برمجة عامة return { increment() { if (count < maxCount) { count++; logCount(); } }, decrement() { if (count > 0) { count--; logCount(); } }, getCount() { return count; }, reset() { count = 0; console.log('Counter reset'); } }; })(); // الاستخدام CounterModule.increment(); // Current count: 1 CounterModule.increment(); // Current count: 2 console.log(CounterModule.getCount()); // 2 // count خاص - لا يمكن الوصول إليه مباشرة console.log(CounterModule.count); // undefined

نمط Revealing Module

نسخة محسّنة من نمط Module تحدد بوضوح ما هو عام:

نمط Revealing Module: const Calculator = (function() { // متغيرات ودوال خاصة let result = 0; function add(x, y) { result = x + y; return result; } function subtract(x, y) { result = x - y; return result; } function multiply(x, y) { result = x * y; return result; } function getResult() { return result; } function reset() { result = 0; } // كشف الدوال العامة return { add, subtract, multiply, getResult, reset }; })(); // الاستخدام Calculator.add(5, 3); // 8 Calculator.multiply(4, 2); // 8 console.log(Calculator.getResult()); // 8 Calculator.reset(); console.log(Calculator.getResult()); // 0

نمط Observer

يحدد تبعية واحد إلى متعدد حيث يمكن لكائنات متعددة مراقبة موضوع:

تطبيق نمط Observer: class Subject { constructor() { this.observers = []; } subscribe(observer) { this.observers.push(observer); } unsubscribe(observer) { this.observers = this.observers.filter(obs => obs !== observer); } notify(data) { this.observers.forEach(observer => observer.update(data)); } } class Observer { constructor(name) { this.name = name; } update(data) { console.log(`${this.name} received: ${data}`); } } // الاستخدام const newsPublisher = new Subject(); const subscriber1 = new Observer('John'); const subscriber2 = new Observer('Jane'); const subscriber3 = new Observer('Bob'); newsPublisher.subscribe(subscriber1); newsPublisher.subscribe(subscriber2); newsPublisher.subscribe(subscriber3); newsPublisher.notify('Breaking News!'); // John received: Breaking News! // Jane received: Breaking News! // Bob received: Breaking News! newsPublisher.unsubscribe(subscriber2); newsPublisher.notify('Another update'); // John received: Another update // Bob received: Another update
الاستخدام في العالم الحقيقي: أنظمة الأحداث، ربط البيانات في الأطر (React، Vue)، أنظمة pub/sub، أنظمة الإشعارات، وإدارة الحالة.

نمط Decorator

يضيف وظائف جديدة للكائنات الموجودة دون تغيير بنيتها:

نمط Decorator: class Coffee { cost() { return 5; } description() { return 'Simple coffee'; } } // دالة Decorator function withMilk(coffee) { const cost = coffee.cost(); const description = coffee.description(); coffee.cost = () => cost + 2; coffee.description = () => `${description}, with milk`; return coffee; } function withSugar(coffee) { const cost = coffee.cost(); const description = coffee.description(); coffee.cost = () => cost + 1; coffee.description = () => `${description}, with sugar`; return coffee; } function withWhippedCream(coffee) { const cost = coffee.cost(); const description = coffee.description(); coffee.cost = () => cost + 3; coffee.description = () => `${description}, with whipped cream`; return coffee; } // الاستخدام let myCoffee = new Coffee(); console.log(myCoffee.description()); // Simple coffee console.log(myCoffee.cost()); // 5 myCoffee = withMilk(myCoffee); myCoffee = withSugar(myCoffee); myCoffee = withWhippedCream(myCoffee); console.log(myCoffee.description()); // Simple coffee, with milk, with sugar, with whipped cream console.log(myCoffee.cost()); // 11

نمط Strategy

يحدد عائلة من الخوارزميات ويجعلها قابلة للتبديل:

نمط Strategy: // فئات الاستراتيجية class CreditCardPayment { pay(amount) { console.log(`Paid $${amount} using Credit Card`); } } class PayPalPayment { pay(amount) { console.log(`Paid $${amount} using PayPal`); } } class CryptoPayment { pay(amount) { console.log(`Paid $${amount} using Cryptocurrency`); } } // السياق class ShoppingCart { constructor(paymentStrategy) { this.paymentStrategy = paymentStrategy; this.amount = 0; } setPaymentStrategy(strategy) { this.paymentStrategy = strategy; } addToCart(price) { this.amount += price; } checkout() { this.paymentStrategy.pay(this.amount); this.amount = 0; } } // الاستخدام const cart = new ShoppingCart(new CreditCardPayment()); cart.addToCart(100); cart.addToCart(50); cart.checkout(); // Paid $150 using Credit Card cart.setPaymentStrategy(new PayPalPayment()); cart.addToCart(75); cart.checkout(); // Paid $75 using PayPal

نمط Command

يغلّف الإجراءات ككائنات، مما يسمح بالمعاملات والطوابير:

نمط Command: // المستقبِل class Light { turnOn() { console.log('Light is ON'); } turnOff() { console.log('Light is OFF'); } } // الأوامر class TurnOnCommand { constructor(light) { this.light = light; } execute() { this.light.turnOn(); } undo() { this.light.turnOff(); } } class TurnOffCommand { constructor(light) { this.light = light; } execute() { this.light.turnOff(); } undo() { this.light.turnOn(); } } // المُستدعي class RemoteControl { constructor() { this.history = []; } execute(command) { command.execute(); this.history.push(command); } undo() { const command = this.history.pop(); if (command) { command.undo(); } } } // الاستخدام const light = new Light(); const remote = new RemoteControl(); const turnOn = new TurnOnCommand(light); const turnOff = new TurnOffCommand(light); remote.execute(turnOn); // Light is ON remote.execute(turnOff); // Light is OFF remote.undo(); // Light is ON remote.undo(); // Light is OFF

مثال من العالم الحقيقي: بناء مدقق النماذج

دمج أنماط متعددة في تطبيق عملي:

مدقق النماذج مع Strategy و Chain of Responsibility: // نمط Strategy - استراتيجيات التحقق المختلفة class RequiredValidator { validate(value) { return value.trim().length > 0 ? null : 'This field is required'; } } class EmailValidator { validate(value) { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(value) ? null : 'Invalid email format'; } } class MinLengthValidator { constructor(minLength) { this.minLength = minLength; } validate(value) { return value.length >= this.minLength ? null : `Minimum length is ${this.minLength}`; } } // فئة الحقل class Field { constructor(name) { this.name = name; this.value = ''; this.validators = []; this.errors = []; } addValidator(validator) { this.validators.push(validator); return this; // تسلسل الدوال } setValue(value) { this.value = value; this.validate(); } validate() { this.errors = []; for (const validator of this.validators) { const error = validator.validate(this.value); if (error) { this.errors.push(error); } } return this.errors.length === 0; } getErrors() { return this.errors; } } // فئة النموذج (نمط Facade) class Form { constructor() { this.fields = {}; } addField(name) { this.fields[name] = new Field(name); return this.fields[name]; } setFieldValue(name, value) { if (this.fields[name]) { this.fields[name].setValue(value); } } validate() { let isValid = true; for (const fieldName in this.fields) { if (!this.fields[fieldName].validate()) { isValid = false; } } return isValid; } getErrors() { const errors = {}; for (const fieldName in this.fields) { const fieldErrors = this.fields[fieldName].getErrors(); if (fieldErrors.length > 0) { errors[fieldName] = fieldErrors; } } return errors; } } // الاستخدام const registrationForm = new Form(); registrationForm .addField('email') .addValidator(new RequiredValidator()) .addValidator(new EmailValidator()); registrationForm .addField('password') .addValidator(new RequiredValidator()) .addValidator(new MinLengthValidator(8)); // تعيين القيم registrationForm.setFieldValue('email', 'invalid-email'); registrationForm.setFieldValue('password', 'short'); // التحقق if (!registrationForm.validate()) { console.log(registrationForm.getErrors()); // { // email: ['Invalid email format'], // password: ['Minimum length is 8'] // } }

تمرين تطبيقي:

المهمة: نفّذ نظام Logger باستخدام أنماط Singleton و Observer:

  1. أنشئ Singleton للـ Logger يمكنه تسجيل الرسائل بمستويات مختلفة (info، warning، error)
  2. أضف مراقبين يتفاعلون مع السجلات (ConsoleObserver، FileObserver)
  3. اسمح بتصفية السجلات حسب المستوى

الحل:

// Singleton Logger class Logger { constructor() { if (Logger.instance) { return Logger.instance; } this.observers = []; Logger.instance = this; } addObserver(observer) { this.observers.push(observer); } log(level, message) { const logEntry = { level, message, timestamp: new Date().toISOString() }; this.observers.forEach(observer => observer.update(logEntry)); } info(message) { this.log('info', message); } warning(message) { this.log('warning', message); } error(message) { this.log('error', message); } } // المراقبون class ConsoleObserver { constructor(minLevel = 'info') { this.levels = { info: 1, warning: 2, error: 3 }; this.minLevel = minLevel; } update(logEntry) { if (this.levels[logEntry.level] >= this.levels[this.minLevel]) { console.log(`[${logEntry.level.toUpperCase()}] ${logEntry.timestamp}: ${logEntry.message}`); } } } class FileObserver { constructor() { this.logs = []; } update(logEntry) { this.logs.push(logEntry); } save() { console.log('Saving logs to file:', this.logs); } } // الاستخدام const logger = new Logger(); const consoleObserver = new ConsoleObserver('warning'); const fileObserver = new FileObserver(); logger.addObserver(consoleObserver); logger.addObserver(fileObserver); logger.info('Application started'); // لا يظهر في الكونسول (أقل من warning) logger.warning('Low memory'); // [WARNING] timestamp: Low memory logger.error('Database connection lost'); // [ERROR] timestamp: Database connection lost fileObserver.save(); // يحفظ جميع السجلات بما في ذلك info

الملخص

في هذا الدرس، تعلمت:

  • أنماط التصميم هي حلول مثبتة للمشاكل البرمجية الشائعة
  • نمط Singleton يضمن وجود مثيل واحد فقط من الفئة
  • نمط Factory ينشئ كائنات دون تحديد فئتها الدقيقة
  • نمط Module يوفر التغليف باستخدام closures
  • نمط Observer يتيح التواصل publish-subscribe
  • نمط Decorator يضيف وظائف دون تعديل الكود الأصلي
  • نمط Strategy يجعل الخوارزميات قابلة للتبديل
  • نمط Command يغلّف الإجراءات ككائنات
التالي: في الدرس التالي، سنستكشف أفضل ممارسات معالجة الأخطاء في JavaScript!