Design Patterns الأساسية — البرمجة الكائنية
أنماط التصميم الأساسية (Design Patterns)
نمط التصميم هو حل مجرب لمشكلة تتكرر في تصميم البرامج. ليس كوداً جاهزاً تنسخه، بل فكرة لها اسم يفهمه المبرمجون: عندما تقول "استخدمت Strategy هنا" يفهم زميلك البنية فوراً.
> تحذير: الأنماط أدوات وليست أهدافاً. لا تضف نمطاً لأن اسمه جميل؛ أضفه عندما تشعر بالمشكلة التي يحلها.
1) Factory: إخفاء طريقة الإنشاء
دالة أو صنف مسؤول عن إنشاء الكائن المناسب حسب مدخل:
def create_notifier(kind):
notifiers = {"email": EmailNotifier, "sms": SmsNotifier}
return notifiers[kind]()بقية الكود لا يحتاج أن يعرف كل الأصناف الموجودة.
2) Strategy: خوارزمية قابلة للتبديل
رأيته في درس التركيب (سياسات الخصم). في Python، لأن الدوال كائنات، كثيراً ما تكون الاستراتيجية مجرد دالة تُمرَّر:
from collections import namedtuple
Product = namedtuple("Product", "name price")
products = [Product("قلم", 2.0), Product("دفتر", 1.5)]
sorted(products, key=lambda p: p.price) # key استراتيجية ترتيب3) Observer: إشعار المهتمين
كائن يحتفظ بقائمة مستمعين ويخطرهم عند حدوث شيء (مثل نظام الأحداث في الواجهات):
class EventBus:
def __init__(self):
self.listeners = []
def subscribe(self, fn):
self.listeners.append(fn)
def publish(self, event):
for fn in self.listeners:
fn(event)المرسل لا يعرف من يستمع، فيمكن إضافة مستمعين جدد دون تعديله.
4) Singleton: نسخة واحدة فقط
يضمن وجود كائن واحد من الصنف (مثل اتصال إعدادات). في Python الوحدة (module) نفسها singleton طبيعي: تُحمَّل مرة واحدة مهما استوردتها. لذلك غالباً يكفي متغير على مستوى الوحدة بدل صنف معقد. Singleton بالأصناف يصعّب الاختبار لأنه حالة عامة مخفية، فاستخدمه نادراً.
5) Decorator (النمط)
تغليف كائن بكائن آخر يضيف سلوكاً بنفس الواجهة. لا تخلطه مع ميزة @decorator في Python (ستتعلمها في الدورة المتقدمة)؛ الفكرة متشابهة لكنها على مستوى الدوال.
ملخص
| النمط | يحل مشكلة |
|---|---|
| Factory | منطق إنشاء متكرر أو معقد |
| Strategy | سلوك يتغير حسب الحالة |
| Observer | إخطار عدة أجزاء بحدث دون ربطها ببعض |
| Singleton | مورد واحد مشترك (استخدمه بحذر) |
تمرين
ابنِ نظام إشعارات: Factory ينشئ نوع الإشعار حسب تفضيل المستخدم، و EventBus يرسل حدث "طلب جديد" إلى مستمعين (تسجيل في سجل، إشعار للمستخدم، تحديث المخزون).
# 1. Singleton Pattern
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connected = False
return cls._instance
def connect(self):
if not self.connected:
print("جاري الاتصال...")
self.connected = True
return self
# نفس الكائن دائماً
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True
# 2. Factory Pattern
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self): pass
class Dog(Animal):
def speak(self): return "هاو هاو!"
class Cat(Animal):
def speak(self): return "مياو!"
class Bird(Animal):
def speak(self): return "تغريد!"
class AnimalFactory:
@staticmethod
def create(animal_type: str) -> Animal:
animals = {
"dog": Dog,
"cat": Cat,
"bird": Bird
}
if animal_type.lower() in animals:
return animals[animal_type.lower()]()
raise ValueError(f"نوع غير معروف: {animal_type}")
# استخدام Factory
animal = AnimalFactory.create("dog")
print(animal.speak()) # هاو هاو!
# 3. Observer Pattern
class Subject:
def __init__(self):
self._observers = []
self._state = None
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def notify(self):
for observer in self._observers:
observer.update(self._state)
def set_state(self, state):
self._state = state
self.notify()
class Observer:
def __init__(self, name):
self.name = name
def update(self, state):
print(f"{self.name} استلم: {state}")
# استخدام Observer
subject = Subject()
obs1 = Observer("المراقب 1")
obs2 = Observer("المراقب 2")
subject.attach(obs1)
subject.attach(obs2)
subject.set_state("حالة جديدة!")
# 4. Strategy Pattern
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount): pass
class CreditCard(PaymentStrategy):
def pay(self, amount):
return f"دفع {amount} بالبطاقة"
class PayPal(PaymentStrategy):
def pay(self, amount):
return f"دفع {amount} بـ PayPal"
class Cash(PaymentStrategy):
def pay(self, amount):
return f"دفع {amount} نقداً"
class ShoppingCart:
def __init__(self, strategy: PaymentStrategy):
self.strategy = strategy
def checkout(self, amount):
return self.strategy.pay(amount)
# تغيير الاستراتيجية
cart = ShoppingCart(CreditCard())
print(cart.checkout(100))
cart.strategy = PayPal()
print(cart.checkout(100))اختبر فهمك