أنماط التصميم (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:
- أنشئ Singleton للـ Logger يمكنه تسجيل الرسائل بمستويات مختلفة (info، warning، error)
- أضف مراقبين يتفاعلون مع السجلات (ConsoleObserver، FileObserver)
- اسمح بتصفية السجلات حسب المستوى
الحل:
// 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!