مبانی و مفاهیم برنامه‌نویسی

برنامه‌نویسی شی‌گرا در پایتون چیست؟ آموزش کامل OOP از مفاهیم اولیه تا کاربردها

برنامه‌نویسی شی‌گرا چیست؟

بیایید برای چند دقیقه به روزهایی برگردیم که نرم‌افزارها هنوز آن‌قدر بزرگ و پیچیده نشده بودند. تصور کنید برنامه‌نویس پایتون هستید که قرار است یک برنامه ساده برای یک فروشگاه بنویسد. قرار است برنامه فقط چند کار انجام دهد:

  • نام کالاها را نگه دارد.
  • قیمت هر کالا را ذخیره کند.
  • تعداد موجودی را مشخص کند.
  • قیمت کل خرید را محاسبه کند.

کار چندان سختی به نظر نمی‌رسد. چند متغیر تعریف می‌کنید، چند تابع می‌نویسید و برنامه را تحویل می‌دهید. مثلاً:

 

product_name = "Laptop"
product_price = 50000000
product_stock = 10

برای محاسبه قیمت هم یک تابع می‌نویسید:

def calculate_price(price, quantity):
    return price * quantity

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

شما هم برنامه را توسعه می‌دهید. متغیرهای بیشتری اضافه می‌کنید. توابع بیشتری می‌نویسید. برنامه همچنان کار می‌کند. اما مشکلات کم‌کم شروع می‌شود.

چالش‌های توسعه نرم‌افزار بدون برنامه‌نویسی شی‌گرا

فرض کنید برای هر محصول این اطلاعات را دارید:

  • نام
  • قیمت
  • موجودی
  • برند
  • دسته‌بندی
  • کد محصول
  • تخفیف

حالا برای مشتری هم اطلاعاتی دارید:

  • نام
  • ایمیل
  • شماره تلفن
  • آدرس
  • برای سفارش:
  • شماره سفارش
  • مشتری
  • محصولات
  • مبلغ
  • وضعیت
  • تاریخ

و برای پرداخت:

  • مبلغ
  • روش پرداخت
  • وضعیت پرداخت
  • شماره تراکنش

هرچه نرم‌افزار بزرگ‌تر می‌شود، داده‌ها و توابع هم بیشتر می‌شوند.

مشکل اصلی دیگر نوشتن کد نیست. مشکل، مدیریت ارتباط بین بخش‌های مختلف برنامه است. یک تغییر کوچک ممکن است روی چندین قسمت دیگر تأثیر بگذارد. مثلاً مدیر فروشگاه می‌گوید: «از این به بعد هر محصول می‌تواند چند نوع تخفیف داشته باشد.»

شما فقط می‌خواهید سیستم تخفیف را تغییر دهید. اما متوجه می‌شوید محاسبه قیمت در چند جای مختلف برنامه انجام شده است. یک تابع قیمت را محاسبه می‌کند. تابع دیگری تخفیف را حساب می‌کند. بخش دیگری موجودی را بررسی می‌کند. جای دیگری اطلاعات محصول ذخیره می‌شود.و همه این قسمت‌ها به یکدیگر وابسته‌اند.

حالا یک سؤال مهم شکل می‌گیرد: آیا راه بهتری برای سازمان‌دهی این برنامه وجود دارد؟

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

شاید تصور کنیم اگر برنامه‌نویس پایتون باتجربه‌تری باشیم، می‌توانیم این مشکل را حل کنیم. تا حدی درست است.می‌توانیم:

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

ما در حال مدل کردن دنیای واقعی هستیم. در دنیای واقعی، «محصول» فقط یک نام و یک قیمت نیست. محصول یک موجودیت است. محصول:

  • ویژگی دارد.
  • وضعیت دارد.
  • رفتار دارد.
  • در طول زمان تغییر می‌کند.

مشتری هم همین‌طور است. سفارش هم همین‌طور. حساب بانکی، خودرو، کارمند، فایل، پیام، کاربر و تقریباً هر موجودیت مهم دیگری در یک نرم‌افزار، « مجموعه‌ای از داده‌ها و رفتارهای مرتبط » است.

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

 

آموزش کامل OOP در زبان برنامه نویسی پایتون

آموزش کامل OOP در زبان برنامه نویسی پایتون

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

فرض کنید اطلاعات یک حساب بانکی را این‌طور نگه می‌داریم:

 

account_owner = "Ali"
balance = 1000000

و برای عملیات حساب تابع‌هایی داریم:

 

def deposit(balance, amount):
    return balance + amount

def withdraw(balance, amount):
    if amount <= balance:
        return balance - amount
    return balance

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

حالا باید مطمئن شویم هر تابع دقیقاً با داده درست کار می‌کند.

  • اگر اشتباهی رخ دهد و تابعی موجودی حساب دیگری را تغییر دهد چه؟
  • اگر قوانین برداشت تغییر کند چه؟
  • اگر بخواهیم رفتار یک نوع حساب خاص با نوع دیگری متفاوت باشد چه؟

هرچه سیستم بزرگ‌تر می‌شود، مدیریت این ارتباط‌ها دشوارتر می‌شود.

اینجا بود که یک ایده بسیار جالب در مهندسی نرم‌افزار شکل گرفت: « چرا داده و رفتار مرتبط با آن را در یک واحد منسجم قرار ندهیم؟» این سؤال، یکی از پایه‌های شکل‌گیری رویکرد شی‌گرا بود.

تغییر تفکر در برنامه‌نویسی شی‌گرا؛ تمرکز بر موجودیت‌ها

برنامه‌نویسان پایتون به جای اینکه فقط بپرسند: چه تابعی باید بنویسم؟ شروع کردند به پرسیدن: در این مسئله چه موجودیت‌هایی داریم؟

در مثال فروشگاه:

  • مشتری
  • محصول
  • سبد خرید
  • سفارش
  • پرداخت

در بانک:

  • مشتری
  • حساب
  • تراکنش
  • کارت
  • وام

در یک سیستم آموزشی:

  • دانشجو
  • استاد
  • درس
  • کلاس
  • امتحان

سپس سؤال بعدی مطرح شد: هر موجودیت چه اطلاعاتی دارد و چه کارهایی انجام می‌دهد؟

مثلاً اطلاعات یک حساب بانکی:

  • صاحب حساب
  • موجودی
  • شماره حساب

رفتار:

  • واریز
  • برداشت
  • نمایش موجودی

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

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

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

 

مفهوم کلاس (Class) و شیء (Object) در برنامه‌نویسی شی‌گرا در پایتون

فرض کنید در برنامه بانکی دو حساب داریم.

حساب علی:

  • صاحب حساب: علی
  • موجودی: 10 میلیون

حساب سارا:

  • صاحب حساب: سارا
  • موجودی: 25 میلیون

هر دو حساب ساختار مشابهی دارند. هر دو موجودی دارند. هر دو صاحب دارند.هر دو می‌توانند پول دریافت کنند. هر دو می‌توانند برداشت کنند. پس می‌توانیم یک قالب مشترک برای آن‌ها داشته باشیم. این قالب همان Class است. و هر حساب واقعی که از روی آن قالب ساخته می‌شود، یک Object است.

به زبان ساده:

Class

قالب ساخت حساب

 

Object

حساب علی

حساب سارا

حساب رضا

این نگاه، برنامه را به دنیای واقعی نزدیک‌تر می‌کند.

هدف اصلی برنامه‌نویسی شی‌گرا؛ شی‌گرایی در پایتون

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

با بزرگ‌تر شدن نرم‌افزارها، نیازهای جدیدی ایجاد شدند:

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

شی‌گرایی مجموعه‌ای از ایده‌ها و ابزارها را برای پاسخ دادن به چنین نیازهایی فراهم کرد.

کنترل مقیاس و توسعه‌پذیری با برنامه‌نویسی شی‌گرا

تصور کنید یک نفر در اتاقش برنامه‌ای می‌نویسد. ابتدا فقط ۱۰۰ خط کد دارد. همه چیز ساده است. بعد برنامه ۱۰۰۰ خط می‌شود. هنوز قابل کنترل است. بعد:

10,000 خط

50,000 خط

100,000 خط

500,000 خط

دیگر نمی‌توان همه چیز را در ذهن نگه داشت. در چنین شرایطی، مسئله اصلی این نیست که برنامه‌نویس بتواند کد بیشتری بنویسد. مسئله این است که « بتواند پیچیدگی را مدیریت کند.» شی‌گرایی یکی از پاسخ‌هایی بود که برای حل این مساله شکل گرفت.

اصل کپسوله‌سازی (Encapsulation) در برنامه‌نویسی شی‌گرا چیست؟

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

اگر موجودی حساب را به صورت یک متغیر پراکنده در برنامه داشته باشیم، قسمت‌های مختلف برنامه می‌توانند مستقیماً آن را تغییر دهند. اما بهتر است حساب خودش مسئول مدیریت موجودی باشد. مثلاً:

class BankAccount:

    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        if amount <= self.balance:
            self.balance -= amount

 

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

درک ساده مفهوم انتزاع و کپسوله‌سازی در پایتون با مثال دنیای واقعی

برای فهم بهتر، یک خودرو را تصور کنید. وقتی پشت فرمان می‌نشینید، لازم نیست بدانید موتور دقیقاً چگونه کار می‌کند. لازم نیست بدانید داخل گیربکس چه اتفاقی می‌افتد. شما با یک رابط ساده کار می‌کنید:

  • فرمان
  • پدال گاز
  • ترمز
  • دنده

جزئیات پیچیده داخل خودرو پنهان شده‌اند.

در طراحی نرم‌افزار هم ایده مشابهی اهمیت پیدا کرد: « هر بخش از سیستم باید بتواند مسئولیت خودش را مدیریت کند و سایر بخش‌ها لازم نباشد از تمام جزئیات داخلی آن خبر داشته باشند.». این طرز فکر به ساخت نرم‌افزارهای قابل مدیریت‌تر کمک می‌کند.

اصول وراثت (Inheritance) و چندریختی (Polymorphism) در برنامه‌نویسی شی‌گرا

حالا تصور کنید در یک سیستم حمل‌ونقل این وسایل را داریم: Car، Motorcycle، Truck، Bus. همه وسیله نقلیه هستند. همه می‌توانند حرکت کنند. اما رفتارشان دقیقاً یکسان نیست.

  • کامیون ظرفیت حمل بار متفاوتی دارد.
  • اتوبوس مسافر حمل می‌کند.
  • موتورسیکلت ویژگی‌های خودش را دارد.

اینجا یک ایده دیگر اهمیت پیدا می‌کند:

می‌توان ویژگی‌های مشترک را در یک ساختار پایه قرار داد و ویژگی‌های اختصاصی را در ساختارهای دیگر توسعه داد. این ایده به مفهوم Inheritance یا وراثت منجر می‌شود.

مثلاً:

Vehicle

┌─────────┼─────────┐

↓         ↓         ↓

Car       Bus      Truck

 

در نتیجه، برنامه‌نویس مجبور نیست ویژگی‌های مشترک را بارها و بارها تکرار کن.

به جای اینکه برای هر وسیله نقلیه، دوباره کدهایی مثل «شتاب گرفتن» و «ترمز کردن» را بنویسیم، یک قالب کلی به نام Vehicle می‌سازیم. Car و Truck هر دو از این قالب ارث می‌برند. یعنی هر چه در Vehicle تعریف شده باشد، به صورت خودکار در بقیه هم هست. این یعنی جلوگیری از تکرار کد و سازماندهی منطقی.

هر وسیله نقلیه «صدای بوق» یا «روش حرکت» متفاوتی دارد. دستور «حرکت کن» را به همه می‌دهیم. ماشین روی چهار چرخ می‌رود، موتور روی دو چرخ و کشتی روی آب. دستور یکی است، اما رفتار هر کدام بر اساس ماهیتش متفاوت است. این مفهوم با عنوان Polymorphism (چندریختی) شناخته می شود.

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

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

شی‌گرایی در پایتون کمک می‌کرد برنامه را به اجزای مشخصی تقسیم کنیم.

  • هر Object مسئولیت‌هایی داشته باشد.
  • داده‌های مرتبط و رفتارهای مرتبط کنار هم باشند.
  • کد قابلیت استفاده مجدد داشته باشد.
  • بخش‌های مختلف سیستم تا حد امکان مستقل‌تر باشند.
  • و نرم‌افزار بتواند بدون بازنویسی کامل، رشد کند.
مهم‌ترین مزایای استفاده از برنامه‌نویسی شی‌گرا در پایتون

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

تاریخچه برنامه‌نویسی شی‌گرا و سیر تحول زبان‌های OOP

ایده‌های شی‌گرایی یک‌شبه و توسط یک نفر اختراع نشدند. ریشه‌های این تفکر به دهه‌های ۱۹۶۰ و ۱۹۷۰ برمی‌گردد. یکی از نقاط مهم تاریخ آن، زبان Simula بود که در دهه ۱۹۶۰ توسعه پیدا کرد. Simula برای شبیه‌سازی سیستم‌های پیچیده طراحی شده بود و مفهوم کلاس و Object را وارد دنیای برنامه‌نویسی کرد.

بعدها زبان Smalltalk این ایده‌ها را بسیار جدی‌تر و گسترده‌تر کرد و نگاه «همه چیز به صورت Object» را به یکی از ویژگی‌های مرکزی زبان تبدیل کرد.

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

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

آیا همیشه باید از برنامه‌نویسی شی‌گرا استفاده کنیم؟

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

اما وقتی مسئله پیچیده‌تر می‌شود و موجودیت‌ها، وضعیت‌ها، رفتارها و ارتباطات زیادی داریم، شی‌گرایی می‌تواند ابزار بسیار قدرتمندی باشد. بنابراین سؤال درست این نیست که: «آیا باید همیشه شی‌گرا برنامه‌نویسی کنیم؟»

سؤال بهتر این است: «برای این مسئله، چه رویکردی باعث می‌شود نرم‌افزار ساده‌تر، قابل فهم‌تر و قابل نگهداری‌تر باشد؟»

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

پایتون یک زبان چندپارادایمی است؛ یعنی شما مجبور نیستید همه برنامه‌ها را به شکل شی‌گرا بنویسید. می‌توانید از توابع و سبک رویه‌ای استفاده کنید.

اما پایتون ابزارهای قدرتمندی برای شی‌گرایی در اختیار شما قرار می‌دهد:

  • Class : برای تعریف کلاس.
  • __init__: برای مقداردهی اولیه Object.
  • Self: برای اشاره به نمونه جاری.

و مفاهیمی مانند:

  • Inheritance
  • Encapsulation
  • Polymorphism
  • Abstraction

برای طراحی ساختارهای پیچیده‌تر.

جمع‌بندی: چرا یادگیری برنامه‌نویسی شی‌گرا برای هر برنامه‌نویس پایتون ضروری است؟

اگر بخواهیم تمام این داستان را در یک جمله خلاصه کنیم، می‌توانیم بگوییم: شی‌گرایی به وجود آمد تا برنامه‌نویسان پایتون بتوانند با افزایش پیچیدگی نرم‌افزار، ساختار برنامه را بهتر سازمان‌دهی و مدیریت کنند.

ما از برنامه‌هایی شروع کردیم که چند متغیر و چند تابع داشتند.

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

و نیاز به راهی برای تقسیم کردن این پیچیدگی به واحدهای قابل مدیریت به وجود آمد. شی‌گرایی در پایتون یکی از پاسخ‌های مهم به این نیاز بود.

در این رویکرد، به جای اینکه فقط مجموعه‌ای از داده‌ها و مجموعه‌ای از توابع داشته باشیم، تلاش می‌کنیم « موجودیت‌های مستقل و معناداری» در نرم‌افزار بسازیم؛ موجودیت‌هایی که داده و رفتار مرتبط با خودشان را در کنار هم دارند.

و اینجا است که مفاهیمی مثل Class، Object،Attribute و Method، معنای واقعی پیدا می‌کنند.

  • کلاس، نقشه ساخت این موجودیت‌هاست.
  • Object، نمونه‌ای واقعی از آن نقشه است.
  • ویژگی‌ها، وضعیت Object را توصیف می‌کنند.
  • متدها، رفتار Object را مشخص می‌کنند.
  • متد سازنده Object را در زمان ایجاد، آماده می‌کند.
  • و `self` به Objectی اشاره می‌کند که در حال حاضر با آن کار می‌کنیم.

پس شی‌گرایی را نباید فقط مجموعه‌ای از دستورهای پایتون بدانیم. « شی‌گرایی یک طرز فکر برای شکستن یک مسئله بزرگ به بخش‌های کوچک‌تر، معنادارتر و قابل مدیریت‌تر است.» و شاید مهم‌ترین چیزی که هنگام یادگیری شی‌گرایی باید به خاطر داشته باشیم همین باشد:

ما کلاس نمی‌سازیم چون پایتون اجازه می‌دهد کلاس بسازیم؛ کلاس می‌سازیم چون می‌خواهیم پیچیدگی مسئله را بهتر مدیریت کنیم.

وقتی این نگاه را درک کنیم، شی‌گرایی دیگر مجموعه‌ای از کلمات عجیب نخواهد بود؛ بلکه تبدیل می‌شود به یک ابزار برای «فکر کردن درباره مساله و طراحی بهتر نرم‌افزار».

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

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *