۱۱

برنامه‌نویسی شیءگرا (OOP) 🏗️

یاد می‌گیریم چطور کد رو مثل دنیای واقعی مدل کنیم!

OOP چیه و چرا مهمه؟

برنامه‌نویسی شیءگرا یه روش فکر کردنه که کد رو مثل دنیای واقعی مدل می‌کنه:

🌟 مفاهیم کلیدی OOP

  • کلاس (Class): قالب یا نقشه برای ساخت آبجکت‌ها
  • آبجکت (Object): نمونه‌ای از کلاس که در حافظه ساخته می‌شه
  • ویژگی (Attribute): خصوصیات آبجکت
  • متد (Method): رفتارها و عملکردهای آبجکت
مثال ساده: کلاس ماشین
# تعریف کلاس
class Car:
    # متد سازنده (Constructor)
    def __init__(self, brand, model, year):
        # ویژگی‌های آبجکت
        self.brand = brand
        self.model = model
        self.year = year
        self.speed = 0
        self.is_running = False
    
    # متدهای کلاس
    def start_engine(self):
        self.is_running = True
        print(f"{self.brand} {self.model} روشن شد! 🚗")
    
    def accelerate(self, amount):
        if self.is_running:
            self.speed += amount
            print(f"سرعت به {self.speed} کیلومتر رسید")
        else:
            print("ابتدا ماشین را روشن کنید!")
    
    def stop(self):
        self.speed = 0
        self.is_running = False
        print(f"{self.brand} {self.model} متوقف شد")
    
    def get_info(self):
        return f"{self.year} {self.brand} {self.model} - سرعت: {self.speed} کیلومتر"

# ساخت آبجکت‌ها (نمونه‌سازی)
my_car = Car("تویوتا", "کمری", 2023)
your_car = Car("بنز", "C200", 2022)

# استفاده از متدها
print("=== ماشین من ===")
print(my_car.get_info())
my_car.start_engine()
my_car.accelerate(50)
my_car.accelerate(30)
print(my_car.get_info())

print("\n=== ماشین شما ===")
print(your_car.get_info())
your_car.accelerate(40)  # بدون روشن کردن موتور
your_car.start_engine()
your_car.accelerate(60)
print(your_car.get_info())

کپسوله‌سازی (Encapsulation)

کپسوله‌سازی یعنی مخفی کردن جزئیات داخلی و کنترل دسترسی به داده‌ها:

Private و Protected Attributes
class BankAccount:
    def __init__(self, account_number, initial_balance=0):
        self.account_number = account_number  # Public
        self._balance = initial_balance       # Protected (با _)
        self.__pin = "1234"                  # Private (با __)
        self.__transaction_history = []      # Private
    
    # Getter برای دسترسی به موجودی
    def get_balance(self):
        return self._balance
    
    # Setter برای تغییر موجودی با اعتبارسنجی
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            self.__add_transaction(f"واریز {amount:,} تومان")
            print(f"✅ {amount:,} تومان واریز شد")
            return True
        else:
            print("❌ مبلغ باید مثبت باشد")
            return False
    
    def withdraw(self, amount, pin):
        # بررسی رمز
        if not self.__verify_pin(pin):
            print("❌ رمز اشتباه است")
            return False
        
        # بررسی موجودی
        if amount > self._balance:
            print("❌ موجودی کافی نیست")
            return False
        
        if amount <= 0:
            print("❌ مبلغ باید مثبت باشد")
            return False
        
        self._balance -= amount
        self.__add_transaction(f"برداشت {amount:,} تومان")
        print(f"✅ {amount:,} تومان برداشت شد")
        return True
    
    # متدهای Private
    def __verify_pin(self, pin):
        return pin == self.__pin
    
    def __add_transaction(self, description):
        from datetime import datetime
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.__transaction_history.append(f"[{timestamp}] {description}")
    
    # متد برای نمایش تاریخچه (با رمز)
    def get_transaction_history(self, pin):
        if self.__verify_pin(pin):
            return self.__transaction_history.copy()
        else:
            return "❌ رمز اشتباه است"
    
    def change_pin(self, old_pin, new_pin):
        if self.__verify_pin(old_pin):
            if len(new_pin) == 4 and new_pin.isdigit():
                self.__pin = new_pin
                self.__add_transaction("تغییر رمز عبور")
                print("✅ رمز با موفقیت تغییر کرد")
                return True
            else:
                print("❌ رمز باید ۴ رقم باشد")
                return False
        else:
            print("❌ رمز فعلی اشتباه است")
            return False
    
    def __str__(self):
        return f"حساب {self.account_number} - موجودی: {self._balance:,} تومان"

# تست کلاس حساب بانکی
account = BankAccount("123456789", 1000000)

print("=== اطلاعات حساب ===")
print(account)
print(f"موجودی: {account.get_balance():,} تومان")

print("\n=== تراکنش‌ها ===")
account.deposit(500000)
account.withdraw(200000, "1234")
account.withdraw(2000000, "1234")  # موجودی کافی نیست
account.withdraw(100000, "0000")   # رمز اشتباه

print("\n=== تغییر رمز ===")
account.change_pin("1234", "5678")
account.withdraw(300000, "5678")

print("\n=== تاریخچه تراکنش‌ها ===")
history = account.get_transaction_history("5678")
for transaction in history:
    print(transaction)

💡 نکات مهم:

  • Public: قابل دسترسی از همه جا
  • Protected (_): فقط در کلاس و زیرکلاس‌ها
  • Private (__): فقط در همان کلاس

وراثت (Inheritance)

وراثت به ما اجازه می‌ده کلاس‌های جدید رو بر اساس کلاس‌های موجود بسازیم:

سلسله مراتب کلاس‌ها
# کلاس پایه (Parent/Base Class)
class Animal:
    def __init__(self, name, species):
        self.name = name
        self.species = species
        self.energy = 100
    
    def eat(self, food):
        self.energy += 20
        print(f"{self.name} {food} خورد و انرژی‌اش به {self.energy} رسید")
    
    def sleep(self):
        self.energy += 30
        print(f"{self.name} خوابید و انرژی‌اش به {self.energy} رسید")
    
    def make_sound(self):
        print(f"{self.name} صدایی می‌کند")
    
    def get_info(self):
        return f"{self.name} ({self.species}) - انرژی: {self.energy}"

# کلاس فرزند (Child/Derived Class)
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name, "سگ")  # فراخوانی سازنده کلاس پدر
        self.breed = breed
        self.loyalty = 100
    
    # Override کردن متد
    def make_sound(self):
        print(f"{self.name} واق واق می‌کند! 🐕")
    
    # متدهای جدید
    def fetch(self, item):
        if self.energy >= 20:
            self.energy -= 20
            self.loyalty += 10
            print(f"{self.name} {item} رو آورد! وفاداری: {self.loyalty}")
        else:
            print(f"{self.name} خسته است و نمی‌تواند بازی کند")
    
    def guard(self):
        if self.energy >= 30:
            self.energy -= 30
            print(f"{self.name} در حال نگهبانی است! 🛡️")
        else:
            print(f"{self.name} برای نگهبانی انرژی کافی ندارد")
    
    # Override کردن get_info
    def get_info(self):
        base_info = super().get_info()
        return f"{base_info} - نژاد: {self.breed} - وفاداری: {self.loyalty}"

class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name, "گربه")
        self.color = color
        self.independence = 80
    
    def make_sound(self):
        print(f"{self.name} میو میو می‌کند! 🐱")
    
    def climb(self):
        if self.energy >= 25:
            self.energy -= 25
            print(f"{self.name} بالا رفت! 🌳")
        else:
            print(f"{self.name} برای بالا رفتن انرژی کافی ندارد")
    
    def hunt(self, prey):
        if self.energy >= 40:
            self.energy -= 40
            self.independence += 5
            print(f"{self.name} {prey} رو شکار کرد! استقلال: {self.independence}")
        else:
            print(f"{self.name} برای شکار انرژی کافی ندارد")
    
    def get_info(self):
        base_info = super().get_info()
        return f"{base_info} - رنگ: {self.color} - استقلال: {self.independence}"

# تست کلاس‌ها
print("=== ساخت حیوانات ===")
dog = Dog("رکس", "ژرمن شپرد")
cat = Cat("میتسی", "سیاه")

animals = [dog, cat]

print("\n=== اطلاعات حیوانات ===")
for animal in animals:
    print(animal.get_info())

print("\n=== صداهای حیوانات ===")
for animal in animals:
    animal.make_sound()

print("\n=== فعالیت‌های خاص ===")
dog.fetch("توپ")
dog.guard()

cat.climb()
cat.hunt("موش")

تمرین عملی: سیستم مدیریت دانشجو

یک سیستم مدیریت دانشجو بنویس که شامل این موارد باشه:

  • کلاس پایه Person با ویژگی‌های مشترک
  • کلاس‌های Student و Teacher که از Person ارث‌بری کنن
  • کلاس Course برای مدیریت دروس
  • امکان ثبت‌نام، حذف و نمره‌دهی
سیستم مدیریت دانشجو
from datetime import datetime
from abc import ABC, abstractmethod

class Person(ABC):
    def __init__(self, name, person_id, email, phone):
        self.name = name
        self.person_id = person_id
        self.email = email
        self.phone = phone
        self.created_at = datetime.now()
    
    @abstractmethod
    def get_role(self):
        pass
    
    def get_info(self):
        return f"{self.get_role()}: {self.name} (ID: {self.person_id})"
    
    def __str__(self):
        return self.get_info()

class Student(Person):
    def __init__(self, name, student_id, email, phone, major):
        super().__init__(name, student_id, email, phone)
        self.major = major
        self.enrolled_courses = {}
        self.gpa = 0.0
    
    def get_role(self):
        return "دانشجو"
    
    def enroll_course(self, course):
        if course.course_id in self.enrolled_courses:
            return False, "قبلاً در این درس ثبت‌نام کرده‌اید"
        
        success, message = course.add_student(self)
        if success:
            self.enrolled_courses[course.course_id] = {
                'course': course,
                'grade': None,
                'attendance': 0
            }
        return success, message
    
    def set_grade(self, course_id, grade):
        if course_id not in self.enrolled_courses:
            return False, "در این درس ثبت‌نام نکرده‌اید"
        
        if not (0 <= grade <= 20):
            return False, "نمره باید بین ۰ تا ۲۰ باشد"
        
        self.enrolled_courses[course_id]['grade'] = grade
        return True, f"نمره {grade} ثبت شد"

class Teacher(Person):
    def __init__(self, name, teacher_id, email, phone, department):
        super().__init__(name, teacher_id, email, phone)
        self.department = department
        self.teaching_courses = []
    
    def get_role(self):
        return f"استاد - {self.department}"
    
    def assign_course(self, course):
        if course not in self.teaching_courses:
            self.teaching_courses.append(course)
            course.teacher = self
            return True, f"درس {course.name} اختصاص یافت"
        return False, "این درس قبلاً اختصاص یافته"

class Course:
    def __init__(self, name, course_id, units, capacity):
        self.name = name
        self.course_id = course_id
        self.units = units
        self.capacity = capacity
        self.enrolled_students = []
        self.teacher = None
    
    def add_student(self, student):
        if len(self.enrolled_students) >= self.capacity:
            return False, "ظرفیت تکمیل است"
        
        self.enrolled_students.append(student)
        return True, f"ثبت‌نام در {self.name} موفق بود"

# تست سیستم
teacher = Teacher("دکتر احمدی", "T001", "ahmadi@uni.ac.ir", "021-1234", "کامپیوتر")
course = Course("برنامه‌نویسی پایتون", "CS101", 3, 30)
student = Student("علی رضایی", "S001", "ali@student.ac.ir", "0912-1234", "کامپیوتر")

teacher.assign_course(course)
student.enroll_course(course)
student.set_grade("CS101", 18)

print(f"استاد: {teacher}")
print(f"دانشجو: {student}")
print(f"درس: {course.name} - ظرفیت: {len(course.enrolled_students)}/{course.capacity}")

چالش پیشرفته: فروشگاه آنلاین

یک سیستم فروشگاه آنلاین طراحی کن با این ویژگی‌ها:

  • کلاس‌های مختلف محصول (کتاب، لباس، الکترونیک)
  • سیستم سبد خرید و پرداخت
  • مدیریت موجودی و تخفیف
  • سیستم امتیازدهی مشتری

💡 این چالش رو خودت حل کن و بعد با راه‌حل مقایسه کن!

نکات برای حل:

  • از Abstract Base Class برای Product استفاده کن
  • هر نوع محصول ویژگی‌های خاص خودش رو داشته باشه
  • سیستم تخفیف رو با Strategy Pattern پیاده کن
  • برای سبد خرید از Composition استفاده کن