برنامه‌نویسی شیءگرا (OOP) در پایتون به زبان ساده: کلاس‌ها، اشیاء و متدها
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 20 دقیقه

برنامه‌نویسی شیءگرا (OOP) در پایتون به زبان ساده: کلاس‌ها، اشیاء و متدها

در دنیای واقعی، ما با اشیاء سر و کار داریم: یک خودرو، یک کتاب، یک کارمند، یا یک حساب بانکی. هر یک از این اشیاء دارای ویژگی‌هایی (مثل رنگ، نام، موجودی) و رفتارهایی (مثل حرکت، چاپ، برداشت وجه) هستند. برنامه‌نویسی شیءگرا (Object-Oriented Programming یا OOP) تلاش می‌کند همین منطق طبیعی را وارد دنیای کدنویسی کند.

در مقابل برنامه‌نویسی رویه‌ای که همه چیز را به صورت توابع و داده‌های جداگانه می‌بیند، OOP داده‌ها و رفتارها را در قالب «کلاس‌ها» و «اشیاء» سازمان‌دهی می‌کند. این رویکرد باعث می‌شود کدها:

  • قابل فهم‌تر و نزدیک‌تر به دنیای واقعی باشند
  • قابلیت استفاده مجدد داشته باشند
  • راحت‌تر توسعه یابند و نگهداری شوند

پایتون یکی از زبان‌هایی است که به‌صورت ساده و قدرتمند از OOP پشتیبانی می‌کند. حتی اگر تازه‌کار باشید، می‌توانید با چند مثال ساده مفاهیم کلاس، شیء و متد را یاد بگیرید و در پروژه‌های خود به کار ببندید.

در این مطلب، گام‌به‌گام با مفاهیم پایه‌ای OOP در پایتون آشنا می‌شویم: از تعریف کلاس و ساخت شیء گرفته تا وراثت، چندریختی و کپسوله‌سازی. هدف ما این است که بدون پیچیدگی‌های فنی، با زبانی ساده و مثال‌های کاربردی، شما را به دنیای شیءگرایی در پایتون وارد کنیم.

مفاهیم پایه‌ای OOP در پایتون

برنامه‌نویسی شیءگرا (Object-Oriented Programming یا OOP) یکی از مهم‌ترین پارادایم‌های برنامه‌نویسی مدرن است که به ما اجازه می‌دهد کدهایی ساخت‌یافته، قابل توسعه و نزدیک به دنیای واقعی بنویسیم. در این بخش، با مفاهیم کلیدی این سبک برنامه‌نویسی آشنا می‌شویم تا درک بهتری از ساختار کلاس‌ها، اشیاء و متدها در پایتون پیدا کنیم.

۱. کلاس (Class) چیست؟

کلاس در واقع یک الگو یا قالب برای ساخت اشیاء است. همان‌طور که یک نقشه‌ی معماری مشخص می‌کند یک ساختمان چه اجزایی دارد، کلاس نیز تعیین می‌کند که اشیاء ساخته‌شده از آن چه ویژگی‌ها و رفتارهایی خواهند داشت. کلاس‌ها به ما کمک می‌کنند تا داده‌ها و توابع مرتبط با آن داده‌ها را در یک ساختار منسجم گرد هم آوریم.

در پایتون، تعریف یک کلاس با کلمه‌ی کلیدی class آغاز می‌شود. درون کلاس می‌توان متغیرهایی (که به آن‌ها ویژگی یا attribute گفته می‌شود) و توابعی (که به آن‌ها متد یا method می‌گوییم) تعریف کرد. این ساختار باعث می‌شود کد ما منظم‌تر، قابل استفاده مجدد و توسعه‌پذیرتر باشد.

class Car:
    color = "red"
    def drive(self):
        print("Driving...")

۲. شیء (Object) چیست؟

شیء نمونه‌ای واقعی از یک کلاس است. وقتی شما یک کلاس تعریف می‌کنید، در واقع فقط یک نقشه ساخته‌اید؛ اما برای استفاده از آن، باید از روی آن شیء بسازید. هر شیء می‌تواند ویژگی‌های خاص خود را داشته باشد، حتی اگر از یک کلاس مشترک ساخته شده باشد.

برای مثال، اگر کلاسی به نام Car تعریف کرده باشید، می‌توانید چندین شیء از آن بسازید: یک ماشین قرمز، یک ماشین آبی، یک ماشین با سرعت بالا و... . هر کدام از این اشیاء مستقل هستند، اما همگی از یک الگو ساخته شده‌اند. این انعطاف‌پذیری یکی از نقاط قوت OOP است.

my_car = Car()
my_car.drive()  # خروجی: Driving...

۳. ویژگی‌ها (Attributes)

ویژگی‌ها اطلاعاتی هستند که درون هر شیء ذخیره می‌شوند. این ویژگی‌ها می‌توانند عمومی (قابل دسترسی از بیرون کلاس) یا خصوصی (محدود به درون کلاس) باشند. برای مثال، رنگ، مدل، یا سال ساخت یک ماشین می‌توانند ویژگی‌های آن باشند.

در پایتون، ویژگی‌ها معمولاً در متد __init__ تعریف می‌شوند که هنگام ساخت شیء اجرا می‌شود. با استفاده از self می‌توان به ویژگی‌های هر شیء دسترسی داشت. این ساختار به ما اجازه می‌دهد هر شیء را با مقادیر دلخواه مقداردهی کنیم و اطلاعات آن را مدیریت نماییم.

print(my_car.color)  # خروجی: red

۴. متدها (Methods)

متدها همان توابعی هستند که درون کلاس تعریف می‌شوند و رفتارهای اشیاء را مشخص می‌کنند. برای مثال، متدی به نام drive() می‌تواند مشخص کند که یک ماشین چگونه حرکت می‌کند. متدها می‌توانند به ویژگی‌های شیء دسترسی داشته باشند و آن‌ها را تغییر دهند.

در پایتون، اولین پارامتر هر متد باید self باشد که به شیء جاری اشاره دارد. این پارامتر به ما امکان می‌دهد به ویژگی‌ها و سایر متدهای همان شیء دسترسی پیدا کنیم. متدها می‌توانند ورودی بگیرند، خروجی بدهند، یا فقط عملی را انجام دهند (مثل چاپ یک پیام).

۵. ارتباط بین کلاس و شیء

رابطه‌ی بین کلاس و شیء را می‌توان با مثال قالب شیرینی و شیرینی واقعی توضیح داد. کلاس همان قالب است که مشخص می‌کند شیرینی چه شکلی باشد، و شیء همان شیرینی واقعی است که از آن قالب ساخته شده است. شما می‌توانید با یک قالب، صدها شیرینی مختلف بسازید، هر کدام با طعم یا رنگ متفاوت.

در برنامه‌نویسی نیز همین‌طور است. یک کلاس تعریف می‌کنید و سپس با استفاده از آن، اشیاء مختلفی می‌سازید. این اشیاء می‌توانند مستقل از هم عمل کنند، اما همگی از یک منطق مشترک پیروی می‌کنند. این ساختار باعث می‌شود کد شما هم قابل توسعه باشد و هم به‌راحتی قابل درک برای دیگران.

 

تعریف کلاس در پایتون

اکنون که با مفاهیم پایه‌ای برنامه‌نویسی شیءگرا آشنا شدیم، وقت آن است که وارد دنیای کدنویسی شویم و اولین کلاس خود را در پایتون تعریف کنیم. در این بخش، یاد می‌گیریم چگونه یک کلاس بسازیم، ویژگی‌ها و متدها را تعریف کنیم، و از متد ویژه‌ی __init__ برای مقداردهی اولیه استفاده کنیم.

۱. ساختار کلی یک کلاس

در پایتون، تعریف کلاس با کلمه‌ی کلیدی class آغاز می‌شود. نام کلاس معمولاً با حرف بزرگ شروع می‌شود و از قواعد نام‌گذاری پیروی می‌کند. درون کلاس می‌توان متدها و ویژگی‌ها را تعریف کرد.

مثال ساده:

class Person:
    pass

در این مثال، کلاسی به نام Person تعریف شده که هنوز هیچ ویژگی یا متدی ندارد. کلمه‌ی pass به پایتون می‌گوید که فعلاً کاری انجام ندهد و فقط ساختار کلاس را حفظ کند.

۲. متد __init__: سازنده‌ی کلاس

متد __init__ یک متد ویژه در پایتون است که هنگام ساخت یک شیء جدید از کلاس، به‌صورت خودکار اجرا می‌شود. این متد برای مقداردهی اولیه به ویژگی‌های شیء استفاده می‌شود.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

در اینجا، کلاس Person دو ویژگی دارد: name و age. وقتی یک شیء از این کلاس ساخته می‌شود، این دو مقدار باید به آن داده شوند. کلمه‌ی کلیدی self به شیء جاری اشاره دارد و برای ذخیره‌سازی اطلاعات درون شیء استفاده می‌شود.

۳. تعریف متدهای ساده

متدها توابعی هستند که درون کلاس تعریف می‌شوند و می‌توانند به ویژگی‌های شیء دسترسی داشته باشند. برای مثال، می‌توان متدی تعریف کرد که اطلاعات شخص را چاپ کند:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        print(f"سلام، من {self.name} هستم و {self.age} سال دارم.")

اکنون می‌توانیم یک شیء از این کلاس بسازیم و متد introduce() را فراخوانی کنیم:

p1 = Person("علی", ۳۰)
p1.introduce()
# خروجی: سلام، من علی هستم و ۳۰ سال دارم.
 

 

ساخت و استفاده از اشیاء در پایتون

پس از تعریف یک کلاس، گام بعدی ساخت اشیاء (objects) از آن کلاس و استفاده از ویژگی‌ها و متدهای آن است. این مرحله، نقطه‌ی ورود به دنیای واقعی برنامه‌نویسی شیءگراست؛ جایی که کلاس‌ها به موجودیت‌های زنده و قابل استفاده تبدیل می‌شوند.

۱. ساخت شیء از یک کلاس

برای ساخت یک شیء، کافی است نام کلاس را مانند یک تابع فراخوانی کنیم. اگر کلاس دارای متد __init__ باشد، باید مقادیر مورد نیاز آن را نیز هنگام ساخت شیء وارد کنیم.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p1 = Person("زهرا", ۲۵)
p2 = Person("مهدی", ۳۲)

در این مثال، دو شیء به نام‌های p1 و p2 از کلاس Person ساخته شده‌اند. هر کدام ویژگی‌های مخصوص به خود را دارند، اما ساختار آن‌ها از یک کلاس مشترک پیروی می‌کند.

۲. دسترسی به ویژگی‌های شیء

پس از ساخت شیء، می‌توان به ویژگی‌های آن دسترسی داشت یا آن‌ها را تغییر داد. این کار با استفاده از نقطه‌گذاری (dot notation) انجام می‌شود.

مثال:

print(p1.name)  # خروجی: زهرا
print(p2.age)   # خروجی: ۳۲

p1.age = ۲۶
print(p1.age)   # خروجی: ۲۶

در اینجا، ابتدا ویژگی‌های name و age را چاپ کردیم و سپس مقدار age را برای شیء p1 تغییر دادیم. این نشان می‌دهد که هر شیء مستقل از دیگری عمل می‌کند.

۳. فراخوانی متدهای شیء

متدهایی که درون کلاس تعریف شده‌اند، می‌توانند توسط اشیاء فراخوانی شوند. این متدها معمولاً با self به ویژگی‌های داخلی شیء دسترسی دارند.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        print(f"سلام، من {self.name} هستم.")

p1 = Person("زهرا", ۲۵)
p1.greet()  # خروجی: سلام، من زهرا هستم.

در این مثال، متد greet() برای شیء p1 فراخوانی شده و از ویژگی name برای چاپ پیام استفاده کرده است.

۴. چند شیء، یک کلاس

یکی از مزایای مهم OOP این است که می‌توان از یک کلاس، چندین شیء با ویژگی‌های متفاوت ساخت. این ویژگی باعث می‌شود بتوانیم مدل‌سازی دقیقی از دنیای واقعی انجام دهیم.

مثال:

p1 = Person("زهرا", ۲۵)
p2 = Person("مهدی", ۳۲)
p3 = Person("سارا", ۲۹)

for person in [p1, p2, p3]:
    person.greet()

خروجی:

سلام، من زهرا هستم.
سلام، من مهدی هستم.
سلام، من سارا هستم.

 

انواع متدها در پایتون: نمونه‌ای، کلاسی و ایستا

در برنامه‌نویسی شیءگرا، متدها نقش مهمی در تعریف رفتار اشیاء دارند. پایتون سه نوع متد اصلی را پشتیبانی می‌کند که هر کدام کاربرد خاصی دارند: متدهای نمونه (instance methods)، متدهای کلاس (class methods)، و متدهای ایستا (static methods). در این بخش، با تفاوت‌ها و کاربردهای هر نوع آشنا می‌شویم.

۱. متدهای نمونه (Instance Methods)

متداول‌ترین نوع متد در پایتون، متدهای نمونه هستند. این متدها به شیء خاصی تعلق دارند و می‌توانند به ویژگی‌ها و سایر متدهای همان شیء دسترسی داشته باشند. پارامتر اول آن‌ها همیشه self است که به شیء جاری اشاره دارد.

مثال:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def describe(self):
        print(f"کتاب '{self.title}' نوشته‌ی {self.author}")

فراخوانی:

b1 = Book("شازده کوچولو", "آنتوان دو سنت اگزوپری")
b1.describe()
# خروجی: کتاب 'شازده کوچولو' نوشته‌ی آنتوان دو سنت اگزوپری

این متد به ویژگی‌های title و author دسترسی دارد و اطلاعات کتاب را چاپ می‌کند.

۲. متدهای کلاس (Class Methods)

متدهای کلاس به خود کلاس تعلق دارند، نه به یک شیء خاص. برای تعریف آن‌ها از دکوریتور @classmethod استفاده می‌شود و پارامتر اول آن‌ها cls است که به خود کلاس اشاره دارد. این متدها معمولاً برای ساخت اشیاء یا تغییرات سطح کلاس استفاده می‌شوند.

مثال:

class User:
    def __init__(self, username):
        self.username = username

    @classmethod
    def create_guest(cls):
        return cls("guest")

فراخوانی:

u1 = User.create_guest()
print(u1.username)  # خروجی: guest

در این مثال، متد create_guest() بدون نیاز به ورودی از بیرون، یک شیء با مقدار پیش‌فرض می‌سازد.

۳. متدهای ایستا (Static Methods)

متدهای ایستا به کلاس یا شیء خاصی وابسته نیستند و فقط یک تابع معمولی درون کلاس هستند. برای تعریف آن‌ها از دکوریتور @staticmethod استفاده می‌شود. این متدها به self یا cls دسترسی ندارند و معمولاً برای انجام محاسبات یا عملیات مستقل استفاده می‌شوند.

مثال:

class MathTools:
    @staticmethod
    def add(a, b):
        return a + b

فراخوانی:

result = MathTools.add(3, 5)
print(result)  # خروجی: 8

در اینجا، متد add() فقط دو عدد را جمع می‌کند و نیازی به ویژگی‌های کلاس ندارد.

۴. مقایسه و کاربردها

نوع متد وابسته به شیء پارامتر اول دکوریتور کاربرد اصلی
نمونه‌ای بله self ندارد تعامل با ویژگی‌های شیء
کلاسی نه (وابسته به کلاس) cls @classmethod ساخت یا تغییر اشیاء سطح کلاس
ایستا نه ندارد @staticmethod عملیات مستقل و عمومی

مفهوم وراثت (Inheritance) در پایتون

وراثت یکی از مهم‌ترین مفاهیم در برنامه‌نویسی شیءگراست. این قابلیت به ما اجازه می‌دهد که یک کلاس جدید (کلاس فرزند) را بر پایه‌ی یک کلاس موجود (کلاس پایه یا والد) بسازیم و ویژگی‌ها و متدهای آن را به ارث ببریم. این کار باعث کاهش تکرار کد، افزایش انسجام و توسعه‌پذیری بهتر برنامه می‌شود.

تعریف کلاس فرزند

برای تعریف یک کلاس فرزند در پایتون، کافی است نام کلاس پایه را در پرانتز کنار نام کلاس جدید بنویسیم. کلاس فرزند به‌صورت خودکار تمام ویژگی‌ها و متدهای کلاس پایه را به ارث می‌برد.

class Employee:
    def __init__(self, name):
        self.name = name

    def work(self):
        print(f"{self.name} در حال کار است.")

class Manager(Employee):
    def manage(self):
        print(f"{self.name} در حال مدیریت تیم است.")

در این مثال، کلاس Manager از کلاس Employee ارث‌بری کرده است. بنابراین، شیء ساخته‌شده از Manager هم می‌تواند متد work() را اجرا کند و هم متد جدید manage() را داشته باشد.

استفاده از super() برای فراخوانی کلاس پایه

گاهی اوقات لازم است در کلاس فرزند، متد __init__ را بازنویسی کنیم اما همچنان بخواهیم از مقداردهی اولیه‌ی کلاس پایه استفاده کنیم. در این حالت از تابع super() استفاده می‌کنیم تا به متدهای کلاس والد دسترسی داشته باشیم.

class Manager(Employee):
    def __init__(self, name, department):
        super().__init__(name)
        self.department = department

    def manage(self):
        print(f"{self.name} مدیر بخش {self.department} است.")

در اینجا، متد __init__ کلاس Manager ابتدا با استفاده از super() ویژگی name را از کلاس پایه مقداردهی می‌کند، سپس ویژگی جدید department را اضافه می‌کند.

بازنویسی متدها (Method Overriding)

کلاس فرزند می‌تواند متدی را که از کلاس پایه به ارث برده، بازنویسی کند. این کار زمانی مفید است که بخواهیم رفتار خاص‌تری برای کلاس فرزند تعریف کنیم.

class Manager(Employee):
    def work(self):
        print(f"{self.name} در حال برنامه‌ریزی جلسات است.")

اکنون اگر متد work() را برای شیء Manager فراخوانی کنیم، نسخه‌ی بازنویسی‌شده اجرا خواهد شد، نه نسخه‌ی کلاس پایه.

چندریختی (Polymorphism) و بازنویسی متدها

چندریختی یکی از مفاهیم بنیادین در برنامه‌نویسی شیءگراست که به ما اجازه می‌دهد متدهایی با نام یکسان اما رفتار متفاوت در کلاس‌های مختلف داشته باشیم. این ویژگی باعث می‌شود کد ما انعطاف‌پذیرتر، قابل توسعه‌تر و ساده‌تر برای استفاده در ساختارهای پیچیده باشد.

چندریختی یعنی چه؟

در لغت، «چندریختی» به معنای داشتن چند شکل یا حالت مختلف است. در برنامه‌نویسی، این مفهوم به این معناست که متدهایی با نام یکسان می‌توانند در کلاس‌های مختلف رفتار متفاوتی داشته باشند. این ویژگی به ما اجازه می‌دهد بدون تغییر در کد فراخوانی، از اشیاء مختلف با رفتارهای خاص خود استفاده کنیم.

برای مثال، فرض کنید چند کلاس مختلف داریم که همگی متدی به نام speak() دارند، اما هر کدام به شیوه‌ی خاص خود آن را پیاده‌سازی کرده‌اند. وقتی این متد را روی هر شیء فراخوانی می‌کنیم، خروجی متناسب با نوع آن شیء خواهد بود.

بازنویسی متدها (Method Overriding)

یکی از روش‌های پیاده‌سازی چندریختی، بازنویسی متدها در کلاس‌های فرزند است. در این حالت، کلاس فرزند متدی را با همان نام و امضای متد کلاس پایه تعریف می‌کند، اما با رفتار متفاوت.

مثال:

class Animal:
    def speak(self):
        print("این حیوان صدا می‌دهد.")

class Dog(Animal):
    def speak(self):
        print("سگ می‌گوید: واق واق!")

class Cat(Animal):
    def speak(self):
        print("گربه می‌گوید: میو میو!")

در این مثال، کلاس‌های Dog و Cat متد speak() را بازنویسی کرده‌اند. حالا اگر از این کلاس‌ها شیء بسازیم و متد speak() را فراخوانی کنیم، خروجی متناسب با نوع حیوان خواهد بود:

animals = [Dog(), Cat()]

for animal in animals:
    animal.speak()

خروجی:

سگ می‌گوید: واق واق!
گربه می‌گوید: میو میو!

همان‌طور که می‌بینید، با وجود اینکه متد speak() در هر دو کلاس یک نام دارد، رفتار آن‌ها متفاوت است. این همان چندریختی است.

مزایای چندریختی

  • ساده‌سازی کدهای پیچیده با استفاده از رابط‌های مشترک
  • امکان گسترش سیستم بدون تغییر در کدهای موجود
  • افزایش خوانایی و انعطاف‌پذیری برنامه

کپسوله‌سازی (Encapsulation) در پایتون

کپسوله‌سازی یکی از اصول مهم برنامه‌نویسی شیءگراست که هدف آن محافظت از داده‌ها و کنترل دسترسی به آن‌هاست. این مفهوم به ما اجازه می‌دهد ویژگی‌ها و رفتارهای داخلی یک شیء را از دسترسی مستقیم بیرونی مخفی کنیم و فقط از طریق متدهای مشخص‌شده با آن‌ها تعامل داشته باشیم.

چرا کپسوله‌سازی مهم است؟

در برنامه‌نویسی، گاهی لازم است داده‌های حساس یا حیاتی را از تغییرات ناخواسته محافظت کنیم. اگر همه‌ی ویژگی‌های یک کلاس به‌صورت عمومی قابل دسترسی باشند، ممکن است برنامه‌نویس یا کاربر ناآگاهانه آن‌ها را تغییر دهد و باعث بروز خطا یا رفتار غیرمنتظره شود. کپسوله‌سازی با محدود کردن دسترسی مستقیم، امنیت و پایداری برنامه را افزایش می‌دهد.

ویژگی‌های خصوصی در پایتون

در پایتون، ویژگی‌هایی که با یک یا دو خط زیر شروع می‌شوند به‌عنوان خصوصی در نظر گرفته می‌شوند. این ویژگی‌ها به‌صورت مستقیم از بیرون کلاس قابل دسترسی نیستند (یا نباید باشند).

  • پیشوند یک خط زیر _attribute: نشان‌دهنده‌ی «غیر عمومی» بودن ویژگی است (توصیه به عدم دسترسی مستقیم).
  • پیشوند دو خط زیر __attribute: باعث «نام‌گذاری مجدد» داخلی می‌شود و دسترسی مستقیم را دشوارتر می‌کند.

مثال:

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # ویژگی خصوصی

    def show_balance(self):
        print(f"موجودی حساب: {self.__balance} تومان")

در این مثال، ویژگی __balance به‌صورت خصوصی تعریف شده و فقط از طریق متد show_balance() قابل مشاهده است.

متدهای getter و setter

برای دسترسی کنترل‌شده به ویژگی‌های خصوصی، از متدهای «گیرنده» (getter) و «تنظیم‌کننده» (setter) استفاده می‌شود. این متدها به ما اجازه می‌دهند مقدار ویژگی را بخوانیم یا تغییر دهیم، اما با کنترل و اعتبارسنجی.

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance

    def get_balance(self):
        return self.__balance

    def set_balance(self, amount):
        if amount >= 0:
            self.__balance = amount
        else:
            print("مقدار موجودی نمی‌تواند منفی باشد.")

اکنون می‌توانیم موجودی را فقط از طریق این متدها بخوانیم یا تغییر دهیم:

acc = BankAccount("رضا", 5000)
print(acc.get_balance())  # خروجی: 5000

acc.set_balance(7000)
print(acc.get_balance())  # خروجی: 7000

acc.set_balance(-1000)    # خروجی: مقدار موجودی نمی‌تواند منفی باشد.

ترکیب (Composition) در مقابل وراثت

در برنامه‌نویسی شیءگرا، علاوه بر وراثت، یک الگوی طراحی مهم دیگر نیز وجود دارد: ترکیب (Composition). این دو رویکرد برای ساختاردهی کلاس‌ها و مدیریت وابستگی‌ها استفاده می‌شوند، اما فلسفه‌ی متفاوتی دارند. در این بخش، با مفهوم ترکیب آشنا می‌شویم و تفاوت آن را با وراثت بررسی می‌کنیم.

ترکیب یعنی چه؟

ترکیب به این معناست که یک کلاس می‌تواند شامل شیءهایی از کلاس‌های دیگر باشد، بدون آن‌که از آن‌ها ارث‌بری کند. به‌عبارت دیگر، به‌جای اینکه کلاس فرزند باشد، یک کلاس «دارای» کلاس دیگر است. این رویکرد باعث انعطاف‌پذیری بیشتر و جداسازی بهتر مسئولیت‌ها می‌شود.

برای مثال، فرض کنید کلاس «ماشین» دارای یک «موتور» است. در این حالت، ماشین از موتور ارث‌بری نمی‌کند، بلکه یک شیء از کلاس موتور را درون خود نگه می‌دارد.

class Engine:
    def start(self):
        print("موتور روشن شد.")

class Car:
    def __init__(self):
        self.engine = Engine()

    def drive(self):
        self.engine.start()
        print("ماشین در حال حرکت است.")

در این مثال، کلاس Car از کلاس Engine ارث‌بری نکرده، بلکه یک شیء از آن را درون خود نگه داشته است. این یعنی ترکیب.

تفاوت ترکیب با وراثت

وراثت زمانی مناسب است که کلاس فرزند «نوعی از» کلاس پایه باشد. مثلاً «مدیر» نوعی «کارمند» است. اما ترکیب زمانی مناسب است که کلاس «دارای» کلاس دیگر باشد. مثلاً «ماشین» دارای «موتور» است.

معیار مقایسه وراثت ترکیب
رابطه «نوعی از» (is-a) «دارای» (has-a)
وابستگی شدید ضعیف و قابل کنترل
انعطاف‌پذیری کمتر (سخت‌تر برای تغییر ساختار) بیشتر (قابل جایگزینی آسان)
استفاده در پایتون با class Child(Base) با ساخت شیء درون کلاس دیگر

مزایای ترکیب

  • جداسازی بهتر مسئولیت‌ها بین کلاس‌ها
  • امکان استفاده مجدد از کلاس‌های مستقل در پروژه‌های مختلف
  • کاهش وابستگی بین کلاس‌ها و افزایش قابلیت تست‌پذیری

نکات حرفه‌ای و توصیه‌های کاربردی در طراحی کلاس‌ها

پس از آشنایی با مفاهیم پایه‌ای برنامه‌نویسی شیءگرا در پایتون، وقت آن است که به نکات حرفه‌ای‌تری بپردازیم؛ نکاتی که رعایت آن‌ها باعث می‌شود کلاس‌هایی تمیز، قابل فهم، قابل نگهداری و مناسب برای پروژه‌های واقعی طراحی کنیم.

نام‌گذاری مناسب کلاس‌ها و متدها

انتخاب نام‌های واضح و معنادار برای کلاس‌ها، متدها و ویژگی‌ها یکی از مهم‌ترین اصول طراحی تمیز است. نام کلاس باید نشان‌دهنده‌ی نوع شیء باشد

(مثلاً Invoice ،User ،Product) و نام متد باید بیانگر عملکرد آن باشد (مثلاً calculate_total ،send_email ،update_status).

از نام‌های عمومی یا مبهم مانند doStuff() یا data() پرهیز کنید. نام خوب، مستندسازی را ساده‌تر و خوانایی کد را افزایش می‌دهد.

استفاده از داک‌استرینگ‌ها برای مستندسازی

پایتون این امکان را فراهم کرده که برای کلاس‌ها و متدها توضیحات داخلی بنویسیم. این توضیحات که به آن‌ها داک‌استرینگ (docstring) گفته می‌شود، به توسعه‌دهندگان دیگر کمک می‌کند تا هدف و نحوه‌ی استفاده از کلاس یا متد را سریع‌تر درک کنند.

مثال:

class Invoice:
    """کلاس مربوط به فاکتور خرید"""

    def calculate_total(self):
        """محاسبه مجموع مبلغ فاکتور"""
        pass

این توضیحات با ابزارهای مستندسازی خودکار نیز قابل استخراج هستند.

تست و اشکال‌زدایی کلاس‌ها

برای اطمینان از عملکرد صحیح کلاس‌ها، باید آن‌ها را تست کنیم. می‌توان از تست‌های دستی یا ابزارهای تست خودکار مانند unittest یا pytest استفاده کرد. تست کردن متدها با ورودی‌های مختلف و بررسی خروجی‌ها، از بروز خطا در مراحل بعدی جلوگیری می‌کند.

مثال ساده با unittest:

import unittest

class TestInvoice(unittest.TestCase):
    def test_calculate_total(self):
        inv = Invoice()
        self.assertEqual(inv.calculate_total(), 0)

رعایت اصول طراحی SOLID به زبان ساده

اصول SOLID مجموعه‌ای از پنج اصل طراحی شیءگرا هستند که رعایت آن‌ها باعث می‌شود کلاس‌ها قابل توسعه، قابل نگهداری و منعطف باشند. این اصول عبارت‌اند از:

  • Single Responsibility Principle – هر کلاس باید فقط یک مسئولیت داشته باشد.
  • Open/Closed Principle – کلاس‌ها باید برای توسعه باز و برای تغییر بسته باشند.
  • Liskov Substitution Principle – کلاس‌های فرزند باید قابل جایگزینی با کلاس پایه باشند.
  • Interface Segregation Principle – کلاس‌ها نباید مجبور به پیاده‌سازی متدهایی شوند که نیاز ندارند.
  • Dependency Inversion Principle – وابستگی‌ها باید به انتزاع باشد، نه به پیاده‌سازی.

در پایتون، رعایت این اصول به‌صورت ساده و عملی امکان‌پذیر است و باعث طراحی حرفه‌ای‌تر می‌شود.

جمع‌بندی و منابع پیشنهادی

در این مطلب، با مفاهیم پایه و کاربردی برنامه‌نویسی شیءگرا (OOP) در پایتون آشنا شدیم. از تعریف کلاس و ساخت اشیاء گرفته تا مفاهیم پیشرفته‌تری مانند وراثت، چندریختی، کپسوله‌سازی و ترکیب، هر بخش به‌صورت گام‌به‌گام و با مثال‌های قابل اجرا ارائه شد تا یادگیری برای فارسی‌زبانان ساده و کاربردی باشد.

مرور مفاهیم کلیدی

  • کلاس‌ها قالب‌هایی هستند برای ساخت اشیاء با ویژگی‌ها و رفتارهای مشخص.
  • اشیاء نمونه‌های واقعی از کلاس‌ها هستند که می‌توانند مستقل عمل کنند.
  • متدها رفتارهای تعریف‌شده در کلاس‌ها هستند و به سه نوع اصلی تقسیم می‌شوند: نمونه‌ای، کلاسی و ایستا.
  • وراثت امکان انتقال ویژگی‌ها و متدها از یک کلاس پایه به کلاس فرزند را فراهم می‌کند.
  • چندریختی اجازه می‌دهد متدهایی با نام یکسان در کلاس‌های مختلف رفتار متفاوتی داشته باشند.
  • کپسوله‌سازی داده‌ها را از دسترسی مستقیم محافظت می‌کند و با متدهای کنترل‌شده مدیریت می‌شود.
  • ترکیب جایگزینی برای وراثت است که وابستگی‌ها را کاهش می‌دهد و طراحی منعطف‌تری ارائه می‌دهد.
  • رعایت اصول طراحی مانند نام‌گذاری مناسب، مستندسازی، تست‌پذیری و اصول SOLID باعث ساخت کلاس‌هایی حرفه‌ای و قابل نگهداری می‌شود.

منابع پیشنهادی برای یادگیری بیشتر

برای ادامه‌ی مسیر یادگیری و تسلط بیشتر بر OOP در پایتون، منابع زیر توصیه می‌شوند:

پیشنهاد تمرین‌های عملی

برای تثبیت یادگیری، پیشنهاد می‌شود:

  • یک کلاس برای «کتابخانه» طراحی کنید که شامل کلاس‌های «کتاب»، «عضو» و «امانت» باشد.
  • پروژه‌ی ساده‌ای مانند «مدیریت وظایف روزانه» با استفاده از کلاس‌ها و متدهای مختلف پیاده‌سازی کنید.
  • متدهای کلاس و ایستا را در پروژه‌های واقعی امتحان کنید تا تفاوت آن‌ها را بهتر درک کنید.

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@arastoo
ارسطو عباسی
کارشناس تولید و بهینه‌سازی محتوا

...

دیدگاه و پرسش
برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

ارسطو عباسی

کارشناس تولید و بهینه‌سازی محتوا