البرمجة الموجهة للكائنات (OOP): الثورة في تنظيم الكود
حتى هذه اللحظة، كنت تكتب برامجك بأسلوب يسمى "البرمجة الإجرائية" (Procedural Programming)، حيث تركز على كتابة دوال تنفذ مهام معينة واحدة تلو الأخرى. لكن عندما تبدأ ببناء تطبيقات حقيقية ضخمة (مثل موقع تجارة إلكترونية أو نظام بنكي)، ستجد أن الكود أصبح متشابكاً وصعب الإدارة. هنا يأتي دور البرمجة الكائنية (Object-Oriented Programming - OOP). هي ليست مجرد طريقة جديدة للكتابة، بل هي فلسفة تعيد صياغة الكود ليكون محاكياً للواقع، مما يجعله أكثر تنظيماً، وقابلية لإعادة الاستخدام، وسهولة في الصيانة.
1. ما هي البرمجة الكائنية (OOP)؟
البرمجة الكائنية هي نمط برمجي يعتمد على مفهوم "الكائنات" (Objects). في العالم الحقيقي، كل شيء حولنا هو كائن: السيارة، الهاتف، الموظف، وحتى الحساب البنكي. كل كائن له خصائص تميزه وأفعال يمكنه القيام بها.
فلسفة OOP: محاكاة الواقع في الكود
الفكرة الأساسية وراء OOP هي أن نفكر في البرنامج كمجموعة من الكائنات التي تتفاعل مع بعضها البعض، تماماً كما يحدث في الحياة الواقعية. بدلاً من كتابة كود طويل يحتوي على مئات الدوال والمتغيرات المتناثرة، نقوم بتجميع البيانات والوظائف المرتبطة معاً في وحدات منطقية تسمى "الكائنات".
كل كائن في الواقع له شيئان أساسيان:
- الخصائص (Attributes/Properties): وهي البيانات التي تصف الكائن. مثلاً، السيارة لها لون، موديل، سرعة قصوى، ورقم لوحة. الموظف له اسم، عمر، راتب، ومنصب وظيفي.
- الأفعال/السلوكيات (Methods/Behaviors): وهي الوظائف التي يمكن للكائن القيام بها. السيارة يمكنها التحرك، التوقف، والتسارع. الموظف يمكنه العمل، أخذ إجازة، والحصول على ترقية.
في OOP، نقوم بتجميع هذه الخصائص والأفعال معاً في وحدة واحدة تسمى الصنف (Class)، ومن ثم ننشئ منها كائنات حقيقية تعمل في البرنامج.
تشبيه المخطط الهندسي والمنزل
لفهم العلاقة بين الصنف (Class) والكائن (Object)، تخيل الأمر كالتالي:
الصنف (Class) هو مثل "المخطط الهندسي" (Blueprint) لبناء منزل. هذا المخطط يحدد:
- عدد الغرف وأحجامها
- مكان المطبخ والحمامات
- مساحة الصالة والشرفات
- نوع المواد المستخدمة
لكن المخطط نفسه ليس منزلاً! لا يمكنك العيش فيه أو استخدامه. إنه مجرد وصف نظري.
الكائن (Object) هو "المنزل الحقيقي" الذي تم بناؤه فعلياً بناءً على ذلك المخطط. يمكنك بناء 100 منزل (100 Object) من نفس المخطط (Class واحد)، وكل منزل سيكون له ساكنوه الخاصون، أثاثه الخاص، ولونه الخاص، لكن جميعها تشترك في نفس التصميم الأساسي.
2. لماذا نحتاج إلى OOP؟ (المشكلة والحل)
المشكلة: حدود البرمجة الإجرائية
عندما تكتب برنامجاً بسيطاً (مثل حاسبة أو برنامج لطباعة جدول الضرب)، البرمجة الإجرائية تكفي تماماً. لكن تخيل أنك تبني نظاماً لإدارة مدرسة بها:
- 1000 طالب، كل طالب له اسم، عمر، صف دراسي، درجات في 10 مواد، وعنوان سكن
- 50 معلم، كل معلم له اسم، تخصص، راتب، وجدول حصص
- 20 صف دراسي، كل صف له رقم، عدد طلاب، ومعلم مسؤول
بالبرمجة الإجرائية، ستحتاج لإنشاء آلاف المتغيرات المنفصلة، وستصبح إدارة هذا الكم الهائل من البيانات كابوساً حقيقياً. لنرى مثالاً بسيطاً:
# البرمجة الإجرائية - مشكلة التشتت
# لنفترض أننا نريد إدارة بيانات 3 طلاب فقط
# بيانات الطالب الأول
student1_name = "أحمد محمد"
student1_age = 20
student1_grade_math = 90
student1_grade_physics = 85
student1_grade_chemistry = 88
student1_address = "القاهرة، شارع النيل"
# بيانات الطالب الثاني
student2_name = "سارة علي"
student2_age = 22
student2_grade_math = 95
student2_grade_physics = 92
student2_grade_chemistry = 94
student2_address = "الإسكندرية، شارع البحر"
# بيانات الطالب الثالث
student3_name = "محمد حسن"
student3_age = 21
student3_grade_math = 78
student3_grade_physics = 82
student3_grade_chemistry = 80
student3_address = "الجيزة، شارع الهرم"
# دوال منفصلة للعمليات
def calculate_average(math, physics, chemistry):
return (math + physics + chemistry) / 3
def print_student_info(name, age, address):
print(f"الاسم: {name}, العمر: {age}, العنوان: {address}")
# استخدام البيانات - لاحظ التعقيد!
avg1 = calculate_average(student1_grade_math, student1_grade_physics, student1_grade_chemistry)
print(f"معدل {student1_name}: {avg1}")
avg2 = calculate_average(student2_grade_math, student2_grade_physics, student2_grade_chemistry)
print(f"معدل {student2_name}: {avg2}")
# المشاكل الواضحة:
# 1. لدينا 18 متغير لـ 3 طلاب فقط! (تخيل لو كان لدينا 1000 طالب)
# 2. لا يوجد رابط واضح بين اسم الطالب ودرجاته
# 3. سهل جداً أن تخطئ وتخلط بين student1_grade_math و student2_grade_math
# 4. إضافة طالب جديد تتطلب كتابة 6 متغيرات جديدة على الأقل
# 5. إذا أردت إضافة معلومة جديدة (مثل رقم الهاتف)، يجب تعديل كل الطلاب
# 6. الكود غير منظم وصعب القراءة والصيانة
الحل: البرمجة الكائنية
الآن لنرى كيف تحل OOP هذه المشاكل بأناقة:
# البرمجة الكائنية - الحل الأنيق
class Student:
"""صنف يمثل طالباً في المدرسة"""
def __init__(self, name, age, address):
# خصائص الطالب
self.name = name
self.age = age
self.address = address
self.grades = {
'math': 0,
'physics': 0,
'chemistry': 0
}
def set_grades(self, math, physics, chemistry):
"""تعيين درجات الطالب"""
self.grades['math'] = math
self.grades['physics'] = physics
self.grades['chemistry'] = chemistry
def get_average(self):
"""حساب المعدل"""
total = sum(self.grades.values())
return total / len(self.grades)
def get_info(self):
"""عرض معلومات الطالب"""
avg = self.get_average()
return f"""
الاسم: {self.name}
العمر: {self.age}
العنوان: {self.address}
المعدل: {avg:.2f}
"""
# إنشاء الطلاب - لاحظ البساطة والوضوح!
student1 = Student("أحمد محمد", 20, "القاهرة، شارع النيل")
student1.set_grades(90, 85, 88)
student2 = Student("سارة علي", 22, "الإسكندرية، شارع البحر")
student2.set_grades(95, 92, 94)
student3 = Student("محمد حسن", 21, "الجيزة، شارع الهرم")
student3.set_grades(78, 82, 80)
# استخدام بسيط ونظيف
print(student1.get_info())
print(student2.get_info())
print(student3.get_info())
# المزايا الواضحة:
# 1. كل طالب هو وحدة واحدة منظمة (Object)
# 2. البيانات والوظائف مرتبطة معاً بشكل منطقي
# 3. إضافة طالب جديد سهلة: سطر واحد فقط!
# 4. الكود واضح وسهل القراءة
# 5. إضافة معلومة جديدة (مثل رقم الهاتف) تتم في مكان واحد فقط
# 6. لا يمكن الخلط بين بيانات الطلاب المختلفين
شرح الكود بالتفصيل:
- السطر 2: نعرّف صنفاً اسمه
Student. هذا هو "القالب" الذي سنستخدمه لإنشاء طلاب. - السطر 5:
__init__هي دالة خاصة تُستدعى تلقائياً عند إنشاء طالب جديد. تشبه "المُنشئ" (Constructor) في اللغات الأخرى. - الأسطر 7-9: نحفظ البيانات الأساسية للطالب باستخدام
self. كلمةselfتعني "هذا الكائن نفسه". - الأسطر 10-14: ننشئ قاموساً لحفظ الدرجات. هذا أفضل من إنشاء متغير منفصل لكل مادة.
- الأسطر 16-20: دالة لتعيين الدرجات. لاحظ أنها "تنتمي" للطالب.
- الأسطر 22-25: دالة لحساب المعدل. تستخدم
self.gradesللوصول لدرجات هذا الطالب بالذات. - السطر 38: ننشئ طالباً جديداً. بايثون تستدعي
__init__تلقائياً وتمرر القيم.
مقارنة شاملة بين الأسلوبين
| الميزة | البرمجة الإجرائية | البرمجة الكائنية (OOP) |
|---|---|---|
| تنظيم البيانات | متغيرات منفصلة ومتناثرة في كل مكان | بيانات مجمعة داخل كائنات منظمة |
| ربط البيانات بالوظائف | لا يوجد رابط واضح، الدوال منفصلة عن البيانات | الدوال (Methods) مرتبطة مباشرة بالبيانات التي تعمل عليها |
| إعادة الاستخدام | صعبة، تتطلب نسخ ولصق الكود | سهلة جداً عبر الوراثة (Inheritance) |
| الصيانة والتطوير | تغيير بسيط قد يكسر أجزاء أخرى من البرنامج | التعديلات محصورة داخل الصنف، أكثر أماناً |
| المشاريع الكبيرة | تصبح معقدة جداً وغير قابلة للإدارة | مثالية لبناء أنظمة ضخمة ومعقدة |
| التعاون بين المبرمجين | صعب، الكود متداخل ومترابط | سهل، كل مبرمج يعمل على صنف مستقل |
| قابلية الاختبار | صعبة، يجب اختبار البرنامج كله | سهلة، يمكن اختبار كل صنف بشكل مستقل |
| الأمان | أي جزء من الكود يمكنه تعديل أي متغير | يمكن حماية البيانات (Encapsulation) |
3. المفاهيم الأساسية في OOP
أ) الصنف (Class) - القالب
الصنف هو تعريف نظري يصف شكل وسلوك مجموعة من الكائنات. إنه مثل "وصفة الطبخ" التي تحدد المكونات والخطوات، لكنها ليست الطبق نفسه. أو مثل "قالب الكعك" الذي يحدد الشكل، لكنه ليس الكعكة.
الصنف يحتوي على:
- الخصائص (Attributes): المتغيرات التي تصف حالة الكائن
- الأساليب (Methods): الدوال التي تحدد سلوك الكائن
ب) الكائن (Object) - النسخة الحقيقية
الكائن هو نسخة فعلية تم إنشاؤها من الصنف. إنه "الطبق الجاهز" الذي طبخته فعلياً باتباع الوصفة. أو "الكعكة الحقيقية" التي صنعتها باستخدام القالب.
كل كائن له:
- هوية فريدة (Identity): عنوان في الذاكرة يميزه عن باقي الكائنات
- حالة (State): قيم فعلية للخصائص
- سلوك (Behavior): القدرة على تنفيذ الأساليب المعرفة في الصنف
class Car:
"""صنف السيارة - هذا هو القالب/المخطط"""
def __init__(self, brand, color, year):
self.brand = brand # الماركة
self.color = color # اللون
self.year = year # سنة الصنع
self.speed = 0 # السرعة الحالية (تبدأ من صفر)
def accelerate(self, amount):
"""زيادة السرعة"""
self.speed += amount
print(f"السيارة {self.brand} تسارعت! السرعة الحالية: {self.speed} كم/س")
def brake(self):
"""الفرامل - توقف كامل"""
self.speed = 0
print(f"السيارة {self.brand} توقفت تماماً")
def get_info(self):
"""عرض معلومات السيارة"""
return f"{self.brand} {self.color} موديل {self.year}"
# الآن لننشئ كائنات (سيارات حقيقية) من هذا الصنف
car1 = Car("تويوتا", "أحمر", 2020)
car2 = Car("هوندا", "أزرق", 2021)
car3 = Car("نيسان", "أبيض", 2019)
# كل سيارة مستقلة تماماً عن الأخرى
print(car1.get_info()) # تويوتا أحمر موديل 2020
print(car2.get_info()) # هوندا أزرق موديل 2021
# يمكننا التحكم في كل سيارة بشكل مستقل
car1.accelerate(50) # السيارة تويوتا تسارعت! السرعة الحالية: 50 كم/س
car1.accelerate(30) # السيارة تويوتا تسارعت! السرعة الحالية: 80 كم/س
car2.accelerate(60) # السيارة هوندا تسارعت! السرعة الحالية: 60 كم/س
# لاحظ: سرعة car1 لم تتأثر بتسارع car2
print(f"سرعة السيارة الأولى: {car1.speed} كم/س") # 80
print(f"سرعة السيارة الثانية: {car2.speed} كم/س") # 60
car1.brake() # السيارة تويوتا توقفت تماماً
print(f"سرعة السيارة الأولى بعد الفرامل: {car1.speed} كم/س") # 0
# كل كائن له عنوان فريد في الذاكرة
print(f"عنوان car1 في الذاكرة: {id(car1)}")
print(f"عنوان car2 في الذاكرة: {id(car2)}")
print(f"هل car1 و car2 نفس الكائن؟ {car1 is car2}") # False
ج) الخصائص (Attributes)
الخصائص هي المتغيرات التي تحمل البيانات داخل الكائن. هناك نوعان:
-
خصائص الكائن (Instance Attributes): خاصة بكل كائن على حدة. مثل
self.brandفي المثال السابق. كل سيارة لها ماركتها الخاصة. - خصائص الصنف (Class Attributes): مشتركة بين جميع الكائنات. مثل "عدد العجلات = 4" لكل السيارات.
د) الأساليب (Methods)
الأساليب هي الدوال التي تحدد ما يمكن للكائن فعله. الفرق بين الدالة العادية والأسلوب:
- الدالة العادية: مستقلة، لا تنتمي لأي كائن
- الأسلوب (Method): ينتمي لصنف معين، ويعمل على بيانات الكائن
كل أسلوب يجب أن يبدأ بمعامل self الذي يمثل الكائن الحالي.
4. الركائز الأربعة للبرمجة الكائنية
تعتمد OOP على أربعة مبادئ أساسية تجعلها قوية ومرنة. سنتعلم كل واحدة منها بالتفصيل في الدروس القادمة، لكن دعنا نفهم الفكرة العامة:
1. التغليف (Encapsulation)
الفكرة: إخفاء التفاصيل الداخلية للكائن وحماية بياناته من التعديل المباشر غير الآمن.
مثال من الحياة: عندما تستخدم جهاز التحكم عن بعد (الريموت) لتشغيل التلفاز، أنت لا تعرف (ولا تحتاج أن تعرف) كيف يعمل الريموت من الداخل. تضغط على زر "التشغيل" فقط، والتفاصيل الداخلية مخفية عنك.
في البرمجة: حساب بنكي لا يجب أن يسمح لأي كود بتعديل الرصيد مباشرة. بدلاً من ذلك، يجب استخدام دوال آمنة مثل deposit() للإيداع و withdraw() للسحب، والتي تتحقق من صحة العملية قبل تنفيذها.
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # خاصية خاصة (محمية)
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"تم إيداع {amount}. الرصيد الجديد: {self.__balance}")
else:
print("المبلغ يجب أن يكون موجباً!")
def withdraw(self, amount):
if amount > 0 and amount <= self.__balance:
self.__balance -= amount
print(f"تم سحب {amount}. الرصيد المتبقي: {self.__balance}")
else:
print("رصيد غير كافي أو مبلغ غير صحيح!")
def get_balance(self):
return self.__balance
account = BankAccount("أحمد", 1000)
account.deposit(500) # طريقة آمنة
account.withdraw(200) # طريقة آمنة
# account.__balance = 999999 # لن يعمل - البيانات محمية!
2. الوراثة (Inheritance)
الفكرة: إنشاء أصناف جديدة تعتمد على أصناف موجودة، مما يسمح بإعادة استخدام الكود ومنع التكرار.
مثال من الحياة: جميع الثدييات تشترك في خصائص معينة (لها شعر، ترضع صغارها، دم حار). الكلب والقطة والإنسان كلهم ثدييات، لكن لكل منهم خصائصه الإضافية الفريدة.
في البرمجة: يمكنك إنشاء صنف عام Employee (موظف) يحتوي على الخصائص المشتركة (الاسم، الراتب، تاريخ التوظيف)، ثم إنشاء أصناف متخصصة مثل Manager (مدير) و Developer (مبرمج) ترث من الصنف الأساسي وتضيف خصائصها الخاصة.
class Animal:
"""الصنف الأساسي - الحيوان"""
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} يأكل")
class Dog(Animal): # الكلب يرث من الحيوان
def bark(self):
print(f"{self.name} ينبح: واف واف!")
class Cat(Animal): # القطة ترث من الحيوان
def meow(self):
print(f"{self.name} تموء: مياو مياو!")
# كل من Dog و Cat ورث دالة eat من Animal
dog = Dog("بوبي")
dog.eat() # بوبي يأكل (ورثها من Animal)
dog.bark() # بوبي ينبح: واف واف! (خاصة بالكلب)
cat = Cat("ميسي")
cat.eat() # ميسي يأكل (ورثها من Animal)
cat.meow() # ميسي تموء: مياو مياو! (خاصة بالقطة)
3. تعدد الأشكال (Polymorphism)
الفكرة: القدرة على استخدام واجهة واحدة لأشكال مختلفة من البيانات. نفس الأمر يمكن أن يؤدي إلى نتائج مختلفة حسب نوع الكائن.
مثال من الحياة: عندما تقول "تحدث!" لكلب، سينبح. وعندما تقول "تحدث!" لقطة، ستموء. وعندما تقول "تحدث!" لإنسان، سيتكلم. نفس الأمر، نتائج مختلفة حسب المستقبل.
في البرمجة: يمكنك إنشاء دالة واحدة تعمل مع أنواع مختلفة من الكائنات، وكل كائن ينفذها بطريقته الخاصة.
class Shape:
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# نفس الدالة (area) تعمل بشكل مختلف حسب نوع الشكل
shapes = [Circle(5), Rectangle(4, 6), Circle(3)]
for shape in shapes:
print(f"المساحة: {shape.area()}")
# النتيجة:
# المساحة: 78.5
# المساحة: 24
# المساحة: 28.26
4. التجريد (Abstraction)
الفكرة: التركيز على "ماذا يفعل" الكائن بدلاً من "كيف يفعل ذلك". إخفاء التعقيد وإظهار الوظائف الأساسية فقط.
مثال من الحياة: عندما تقود سيارة، تستخدم المقود والفرامل ودواسة الوقود. لا تحتاج أن تعرف كيف يعمل المحرك من الداخل، أو كيف يتم نقل الحركة للعجلات. التفاصيل المعقدة مخفية، وأنت تتعامل مع واجهة بسيطة.
في البرمجة: عندما تستخدم دالة send_email()، لا تحتاج أن تعرف كيف يتم إرسال البريد الإلكتروني (بروتوكولات SMTP، تشفير، إلخ). تستدعي الدالة فقط وتثق أنها ستنجز المهمة.
5. متى يجب عليك استخدام OOP؟
بايثون لغة مرنة جداً، ويمكنك كتابة برامج بسيطة بدون OOP. لكن هناك حالات تكون فيها OOP هي الخيار الأفضل (أو الوحيد):
استخدم OOP عندما:
- المشروع متوسط أو كبير الحجم: إذا كان البرنامج يحتوي على أكثر من 500 سطر من الكود، أو يعمل عليه أكثر من مبرمج واحد.
- تكرار نمط معين من البيانات: إذا كان لديك عدة كيانات متشابهة (مثل مستخدمين، منتجات، طلبات، إلخ).
- بناء مكتبات أو أطر عمل: جميع الأطر الشهيرة (Django, Flask, FastAPI) تعتمد كلياً على OOP.
- الحاجة لإعادة استخدام الكود: عندما تريد تجنب نسخ ولصق نفس الكود في أماكن متعددة.
- التعامل مع بيانات معقدة: عندما تكون البيانات لها علاقات معقدة (مثل: طالب ينتمي لصف، والصف له معلم، والمعلم له جدول حصص).
- تطوير ألعاب: كل عنصر في اللعبة (لاعب، عدو، سلاح، خريطة) يمكن أن يكون صنفاً.
لا تستخدم OOP عندما:
- السكربتات البسيطة: برنامج من 20-50 سطر لمهمة واحدة بسيطة (مثل إعادة تسمية ملفات).
- معالجة البيانات الخطية: قراءة ملف، معالجة بسيطة، كتابة النتيجة.
- التعلم الأولي: إذا كنت مبتدئاً تماماً، ابدأ بالبرمجة الإجرائية أولاً.
text = "hello"
print(text.upper()) # HELLO
أنت في الحقيقة تستدعي أسلوباً (upper) من صنف النصوص (str). بايثون مبنية على OOP من الأساس!
6. مثال عملي شامل: نظام مكتبة
لنطبق ما تعلمناه في مثال واقعي: نظام بسيط لإدارة مكتبة.
class Book:
"""صنف يمثل كتاباً في المكتبة"""
def __init__(self, title, author, isbn, copies):
self.title = title # عنوان الكتاب
self.author = author # المؤلف
self.isbn = isbn # رقم ISBN
self.total_copies = copies # عدد النسخ الكلي
self.available_copies = copies # النسخ المتاحة
def borrow(self):
"""استعارة نسخة من الكتاب"""
if self.available_copies > 0:
self.available_copies -= 1
print(f"تم استعارة '{self.title}'. النسخ المتبقية: {self.available_copies}")
return True
else:
print(f"عذراً، '{self.title}' غير متاح حالياً")
return False
def return_book(self):
"""إرجاع نسخة من الكتاب"""
if self.available_copies < self.total_copies:
self.available_copies += 1
print(f"تم إرجاع '{self.title}'. النسخ المتاحة: {self.available_copies}")
else:
print(f"خطأ: جميع نسخ '{self.title}' موجودة بالفعل")
def get_info(self):
"""عرض معلومات الكتاب"""
status = "متاح" if self.available_copies > 0 else "غير متاح"
return f"""
{self.title}
المؤلف: {self.author}
ISBN: {self.isbn}
النسخ: {self.available_copies}/{self.total_copies} متاحة
الحالة: {status}
"""
class Member:
"""صنف يمثل عضواً في المكتبة"""
def __init__(self, name, member_id):
self.name = name
self.member_id = member_id
self.borrowed_books = [] # قائمة الكتب المستعارة
def borrow_book(self, book):
"""استعارة كتاب"""
if book.borrow():
self.borrowed_books.append(book)
print(f"{self.name} استعار '{book.title}'")
def return_book(self, book):
"""إرجاع كتاب"""
if book in self.borrowed_books:
book.return_book()
self.borrowed_books.remove(book)
print(f"{self.name} أرجع '{book.title}'")
else:
print(f"{self.name} لم يستعر '{book.title}'")
def show_borrowed_books(self):
"""عرض الكتب المستعارة"""
if self.borrowed_books:
print(f"\nالكتب المستعارة من قبل {self.name}:")
for book in self.borrowed_books:
print(f" - {book.title}")
else:
print(f"{self.name} لم يستعر أي كتب حالياً")
# استخدام النظام
print("=" * 50)
print("نظام إدارة المكتبة")
print("=" * 50)
# إنشاء كتب
book1 = Book("تعلم Python", "جون سميث", "978-1234567890", 3)
book2 = Book("البرمجة الكائنية", "جين دو", "978-0987654321", 2)
book3 = Book("هياكل البيانات", "بوب جونسون", "978-1122334455", 1)
# إنشاء أعضاء
member1 = Member("أحمد محمد", "M001")
member2 = Member("سارة علي", "M002")
# عمليات الاستعارة
print("\n--- عمليات الاستعارة ---")
member1.borrow_book(book1) # أحمد يستعير كتاب Python
member1.borrow_book(book2) # أحمد يستعير كتاب OOP
member2.borrow_book(book1) # سارة تستعير كتاب Python
member2.borrow_book(book3) # سارة تستعير كتاب هياكل البيانات
# محاولة استعارة كتاب غير متاح
member1.borrow_book(book3) # الكتاب مستعار بالفعل
# عرض الكتب المستعارة
member1.show_borrowed_books()
member2.show_borrowed_books()
# عرض معلومات الكتب
print("\n--- حالة الكتب في المكتبة ---")
print(book1.get_info())
print(book2.get_info())
print(book3.get_info())
# إرجاع كتاب
print("\n--- عمليات الإرجاع ---")
member1.return_book(book1)
member2.return_book(book3)
# عرض الحالة النهائية
print("\n--- الحالة النهائية ---")
member1.show_borrowed_books()
print(book1.get_info())
ما الذي يجعل هذا المثال قوياً؟
- تنظيم واضح: كل شيء في مكانه المنطقي. الكتاب له خصائصه ووظائفه، والعضو له خصائصه ووظائفه.
- سهولة التوسع: يمكنك بسهولة إضافة صنف
Libraryلإدارة مجموعة من الكتب والأعضاء. - إعادة الاستخدام: يمكنك إنشاء مئات الكتب والأعضاء من نفس الأصناف.
- الصيانة: إذا أردت تغيير طريقة عرض معلومات الكتاب، تعدل دالة
get_infoفقط.
ملخص شامل للدرس
- OOP: نمط برمجي يجمع البيانات (Attributes) والوظائف (Methods) في كائنات تحاكي الواقع.
- Class (الصنف): القالب أو المخطط الذي يحدد شكل وسلوك الكائنات.
- Object (الكائن): النسخة الحقيقية المنشأة من الصنف، لها هوية وحالة وسلوك.
- الركائز الأربعة: التغليف، الوراثة، تعدد الأشكال، والتجريد.
- المزايا: تنظيم أفضل، إعادة استخدام الكود، سهولة الصيانة، ملاءمة للمشاريع الكبيرة.
- بايثون: لغة كائنية بالكامل، كل شيء فيها (حتى الأرقام والنصوص) هو كائن.
- متى تستخدم OOP: المشاريع المتوسطة والكبيرة، الأنظمة المعقدة، بناء المكتبات والأطر.
الخطوة التالية
الآن بعد أن فهمنا لماذا نستخدم OOP وما هي مفاهيمها الأساسية، لننتقل إلى الجانب العملي ونتعلم كيف ننشئ أول صنف (Class) لنا في بايثون بالتفصيل.
الدرس التالي: إنشاء الأصناف (Classes) في Python