Kodax

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))
اختبر فهمك