البرمجة كائنية التوجه
في هذا الدرس سنتعلم أسس البرمجة كائنية التوجه (OOP): الفئات والكائنات، الوراثة، تعدد الأشكال، التغليف، والتجريد - المفاهيم الأساسية لبناء برامج منظمة وقابلة للتطوير.
🎯 مقدمة في OOP
البرمجة كائنية التوجه (Object-Oriented Programming) هي نمط برمجي قوي لهيكلة البرامج عن طريق إنشاء "كائنات" (Objects) تغلف بداخلها البيانات (السمات - Attributes) والوظائف (الطرق - Methods).
💡 الهدف من OOP
✅ نمذجة كيانات العالم الحقيقي وتفاعلاتها
✅ جعل الكود أكثر تنظيماً ووضوحاً
✅ قابلية لإعادة الاستخدام (Reusability)
✅ سهولة في الصيانة والتطوير
🏗️ الفئات والكائنات (Classes & Objects)
الفئة (Class): هي المخطط (Blueprint) الذي يحدد السمات والوظائف التي ستمتلكها الكائنات.
الكائن (Object): هو نسخة فعلية (Instance) من الفئة، يحمل بيانات خاصة به.
# تعريف الفئة
class Car:
def __init__(self, brand, model):
self.brand = brand # سمة
self.model = model # سمة
def display_info(self):
# طباعة التفاصيل باستخدام دمج النصوص التقليدي
print("Car: " + self.brand + " " + self.model)
# إنشاء كائن (Instantiation)
my_car = Car("Toyota", "Corolla")
my_car.display_info()
class Car:
"""Blueprint for creating car objects."""
def __init__(self, brand: str, model: str):
self.brand = brand
self.model = model
def __str__(self):
# استخدام دالة __str__ لتمثيل الكائن نصياً
return f"Car: {self.brand} {self.model}"
# الاستخدام
my_car = Car("Toyota", "Corolla")
print(my_car) # سيتم استدعاء __str__ تلقائياً
__init__ هو المشيد (Constructor) - دالة خاصة تُستدعى تلقائياً عند إنشاء كائن جديد.
المعامل self يشير إلى الكائن نفسه.
⚙️ سمات الفئة مقابل سمات الكائن
سمات الكائن (Instance Attributes): تُعرف باستخدام self.name، وهي خاصة بكل كائن على حدة.
سمات الفئة (Class Attributes): تُعرف مباشرة داخل الفئة، وهي مشتركة بين جميع الكائنات.
class Car:
wheels = 4 # سمة مشتركة (للفئة)
def __init__(self, color):
self.color = color # سمة خاصة (للكائن)
car1 = Car("Red")
car2 = Car("Blue")
print(car1.wheels) # 4
print(car1.color) # Red
print(car2.color) # Blue
class Car:
num_wheels: int = 4 # Class Attribute
def __init__(self, color: str):
self.color = color # Instance Attribute
car1 = Car("Red")
# الوصول لسمة الفئة عبر اسم الفئة لضمان الوضوح
print(f"All cars have {Car.num_wheels} wheels.")
print(f"This car is {car1.color}.")
🧬 الوراثة (Inheritance)
تسمح الوراثة لفئة فرعية (Child Class) باكتساب خصائص وطرق فئة رئيسية (Parent Class). هذا يعزز إعادة استخدام الكود ويدعم العلاقات الهرمية.
class Animal:
def speak(self):
print("This animal makes a sound")
# الوراثة: Dog يرث من Animal
class Dog(Animal):
def speak(self): # تعديل السلوك (Overriding)
print("Bark!")
dog = Dog()
dog.speak() # Bark!
class Animal:
def speak(self):
return "Generic Sound"
class Dog(Animal):
def speak(self):
# يمكن استدعاء دالة الأب إذا لزم الأمر، أو تجاوزها تماماً
return "Bark!"
def animal_sound(animal: Animal):
print(animal.speak())
animal_sound(Dog()) # Bark!
💡 فوائد الوراثة
✅ إعادة استخدام الكود (لا حاجة لتكرار الطرق المشتركة)
✅ إنشاء علاقات هرمية واضحة
✅ سهولة التوسع والتعديل
🎭 تعدد الأشكال (Polymorphism)
يعني "نفس العملية، سلوك مختلف". يسمح لدوال بنفس الاسم بالعمل بطرق مختلفة بناءً على نوع الكائن.
class Cat:
def speak(self):
return "Meow"
class Dog:
def speak(self):
return "Bark"
animals = [Cat(), Dog()]
for animal in animals:
print(animal.speak())
class Bird:
def fly(self):
print("Flying high")
class Airplane:
def fly(self):
print("Taking off")
# دالة تقبل أي كائن يمتلك دالة fly
def perform_flight(entity):
if hasattr(entity, 'fly'): # التحقق من وجود الدالة
entity.fly()
perform_flight(Bird()) # Flying high
perform_flight(Airplane()) # Taking off
🔒 التغليف (Encapsulation)
تجميع البيانات والدوال داخل فئة، مع تقييد الوصول لبعض المكونات لحمايتها.
| النوع | الصيغة | الوصول |
|---|---|---|
| Public | self.name |
متاح للجميع |
| Protected | self._name |
للفئة وفروعها |
| Private | self.__name |
للفئة فقط |
class Student:
def __init__(self, age):
self._age = age # متغير محمي
def get_age(self):
return self._age
def set_age(self, age):
if age > 0:
self._age = age
else:
print("Invalid age")
s = Student(20)
s.set_age(25)
print(s.get_age())
class Student:
def __init__(self, age: int):
self._age = age
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value <= 0:
raise ValueError("Age must be positive")
self._age = value
s = Student(20)
s.age = 25 # يبدو كأنه متغير عادي، لكنه يستدعي الـ setter
print(s.age)
🎨 التجريد (Data Abstraction)
إخفاء تفاصيل التنفيذ المعقدة وإظهار الوظائف الضرورية فقط. يتم ذلك باستخدام الفئات المجردة (Abstract Classes) التي تحتوي على طرق مجردة يجب تنفيذها في الفئات الفرعية.
from abc import ABC, abstractmethod
class Shape(ABC): # فئة مجردة
@abstractmethod
def area(self):
pass
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self): # يجب تنفيذ الدالة هنا
return self.side * self.side
sq = Square(4)
print(sq.area()) # 16
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def sound(self):
"""Subclasses must implement this method"""
pass
class Lion(Animal):
def sound(self):
return "Roar"
# لا يمكن إنشاء كائن من Animal مباشرة لأنه مجرد
# a = Animal() # سيسبب خطأ
l = Lion()
print(l.sound()) # Roar
💡 فوائد التجريد
✅ إجبار الفئات الفرعية على تنفيذ الطرق المطلوبة
✅ إنشاء واجهات موحدة (Interfaces)
✅ التركيز على "ماذا يفعل" وليس "كيف يفعله"