پاسخ تمرین‌های فصل سوم: تفکر شیءگرا

Please login to bookmark Close

پاسخ‌ها به‌ترتیب پرسش‌های فایل 03-oo-thinking-exercise.md. جایی که راه‌حل دیگری هم ممکن بود، بخش راه دیگر اضافه شده تا زاویه‌های مختلف را ببینید.

تمرین ۱ – پرسش‌های مفهومی

۱. هر دو class را بلدند؛ پس تفاوت در دانش سینتکس نیست. تفاوت در ذهنیت است: طراح شیءگرا پیش از تایپ اولین حرف، مسئله را در ذهنش به موجودیت‌ها، مسئولیت‌ها و ارتباط‌ها تجزیه کرده است. تازه‌کار بلافاصله شروع به نوشتن توابع می‌کند و در نهایت به «یک مشت تابع داخل یک کلاس» می‌رسد که فقط ظاهرش شیءگراست. به‌بیان دیگر، تازه‌کار می‌پرسد «چه مراحلی را کد کنم؟» ولی طراح می‌پرسد «چه موجودیت‌هایی درگیرند و مسئولیت هرکدام چیست؟».

۲. در یک رستوران اگر یک نفر همه‌ی کارها را انجام دهد (سفارش، آشپزی، ظرف‌شویی، حساب)، با اولین شلوغی فرومی‌پاشد. رستوران واقعی کار می‌کند چون هر نقش مسئولیت مشخصی دارد و همه با هم تعامل می‌کنند. طراحی شیءگرا هم همین است: به‌جای یک موجود همه‌کاره، مجموعه‌ای از موجودیت‌های متخصص. کل هنر شیءگرایی در پاسخ به این یک سوال خلاصه می‌شود: «این مسئولیت مال چه کسی است؟»

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

  • اسم‌ها (مشتری، سفارش، فاکتور) کاندیدای کلاس یا attribute هستند.
  • فعل‌ها (سفارش‌دادن، پرداختن، محاسبه‌کردن) کاندیدای متد هستند.

۴. چون هر اسمی سزاوار کلاس‌شدن نیست. «قیمت» یک اسم است، اما آن‌قدر ساده و کم‌مسئولیت است که فقط یک عدد (attribute) می‌شود، نه یک کلاس مستقل. اگر کورکورانه هر اسم را کلاس کنیم، ده‌ها کلاس بی‌خاصیت می‌سازیم. قضاوت شما تعیین می‌کند کدام اسم آن‌قدر مهم و پرمسئولیت است که کلاس شود. استراتژی اسم/فعل نقطه‌ی شروع است تا فهرست کاندیداها را بسازید، اما پالایش نهایی با شماست.

۵. «مسئولیت» یعنی کاری که یک کلاس متعهد به انجامش است. یک کلاس خوب، یک مسئولیت منسجم دارد؛ یعنی همه‌ی متدها و داده‌هایش حول یک هدف می‌چرخند. سوال کلیدی برای سنجش: «این کلاس چه دلیلی برای تغییر دارد؟» اگر بیش از یک دلیل کاملاً متفاوت پیدا کردید (مثلاً کلاسی که هم منطق سفارش را نگه می‌دارد و هم ایمیل می‌فرستد → دو دلیل مستقل: تغییر قوانین سفارش، و تغییر سیستم ایمیل)، احتمالاً بیش از یک مسئولیت دارد و باید شکسته شود.

تمرین ۲ – God Object و تشخیص کلاس/تابع

۶. God Object («شیء خدا») کلاسی است که می‌خواهد همه‌کار انجام دهد و همه‌چیز را بداند. مشکلاتش:

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

۷. نشانه‌های کلاس:

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

نشانه‌های تابع:

  • یک ورودی می‌گیرید، یک خروجی می‌دهید و حالتی برای نگه‌داشتن نیست (تبدیل واحد دما).
  • منطق، بی‌حالت (Stateless) و مستقل است.

قاعده‌ی سرانگشتی: اگر داده و رفتار باید با هم زندگی کنند و حالت نگه دارند → کلاس؛ اگر فقط یک تبدیل بی‌حالت است → تابع.

۸.

  • حالت (State): داده‌ای که شیء در خود نگه می‌دارد و در طول زمان تغییر می‌کند (موجودی حساب، آیتم‌های سبد). State در attributeها زندگی می‌کند.
  • رفتار (Behavior): کارهایی که شیء می‌تواند انجام دهد و معمولاً حالتش را می‌خوانند یا تغییر می‌دهند. Behavior در متدها زندگی می‌کند.

قدرت شیءگرایی در این است که رفتار (مثل deposit) نیازی ندارد کسی موجودی را به آن پاس بدهد؛ خودش از طریق self.balance به حالت دسترسی دارد.

تمرین ۳ – کدنویسی و تحلیل

۹. تحلیل سیستم پارکینگ:

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

پالایش — کدام کلاس، کدام attribute:

نامزدنتیجهچرا
کاربر (User)کلاسموجودیت مستقل، چند نمونه دارد
پارکینگ (ParkingLot)کلاسهماهنگ‌کننده‌ی کل سیستم
جای‌پارک (Slot)کلاسحالت دارد (خالی/اشغال)، رفتار دارد
خودرو (Car)کلاس (یا attribute ساده)اگر فقط پلاک مهم است می‌تواند attribute باشد
شماره (number)attributeفقط یک عدد روی Slot
مدت‌زمان (duration)attributeیک مقدار عددی
هزینه (fee)attribute یا خروجی متدنتیجه‌ی محاسبه است
رسید (Receipt)کلاس (یا attribute)اگر ساختار دارد، کلاس

یک طرح ممکن:

class Slot:                     # important, stateful noun → class
    def __init__(self, number):
        self.number = number    # simple noun → attribute
        self.occupied = False   # state: free or occupied
    def is_free(self):          # verb → method
        return not self.occupied

class ParkingLot:               # coordinator → class
    def __init__(self):
        self.slots = []
    def park(self, car, slot): ...        # verb "to park"
    def calculate_fee(self, duration): ... # verb "to compute"
    def issue_receipt(self): ...      # verb "to issue"

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

۱۰. بازآرایی BlogSystem به کلاس‌های متخصص. هر کلاس یک مسئولیت:

class User:                 # managing user identity
    def write_post(self, text): ...

class Post:                 # the post itself and its data
    def __init__(self, text):
        self.text = text

class HtmlRenderer:         # only conversion to HTML
    def render(self, post): ...

class EmailService:         # only sending email
    def send(self, user, msg): ...

class PostRepository:       # only save/load from the database
    def save(self, post): ...

class ReadingTimeCalculator:   # only computing reading time
    def estimate(self, text): ...

هر مشکل سیستم حالا در یک کلاس کوچک و مستقل زندگی می‌کند: تغییر قالب HTML فقط HtmlRenderer را دست می‌زند، تغییر دیتابیس فقط PostRepository را.

راه دیگر: اگر پروژه کوچک است، شاید calculate_reading_time نیازی به یک کلاس کامل نداشته باشد و یک تابع ساده‌ی بی‌حالت کافی باشد (def reading_time(text): ...). این دقیقاً همان «کلاس یا تابع؟» است — محاسبه‌ی زمان مطالعه بی‌حالت است، پس تابع هم انتخاب درستی است. اینجا Tradeoff بین «یکدستی همه‌چیز کلاس باشد» و «سادگی تابع» وجود دارد.

۱۱.

  • الف) تابع. تبدیل ارز با نرخ ثابت بی‌حالت است: ورودی می‌گیرد، خروجی می‌دهد، چیزی برای نگه‌داشتن نیست.
  • ب) کلاس. سبد خرید حالت دارد (فهرست آیتم‌ها) که در طول زمان تغییر می‌کند.
  • ج) تابع. میانگین‌گرفتن یک تبدیل بی‌حالت ورودی→خروجی است.
  • د) کلاس. بازیکن چند حالت به‌هم‌مرتبط دارد (جان، امتیاز، موقعیت) که در طول بازی تغییر می‌کنند و رفتارها آن‌ها را می‌خوانند/می‌نویسند.

۱۲. کلاس Counter:

class Counter:
    def __init__(self):
        self.value = 0          # State: the counter's state

    def increment(self):        # Behavior
        self.value += 1         # behavior changes the state

    def reset(self):            # Behavior
        self.value = 0

استفاده:

c = Counter()
c.increment()
c.increment()
print(c.value)   # 2
c.reset()
print(c.value)   # 0
  • State: self.value — تنها داده‌ای که شیء نگه می‌دارد.
  • Behavior: increment و reset — متدهایی که حالت را تغییر می‌دهند.

راه دیگر (پذیرش مقدار اولیه‌ی دلخواه و گام دلخواه): می‌توان انعطاف بیشتری داد:

class Counter:
    def __init__(self, start=0, step=1):
        self.value = start
        self.step = step

    def increment(self):
        self.value += self.step

    def reset(self):
        self.value = 0

این نسخه هم‌چنان یک مسئولیت دارد (شمارش)، اما قابل‌تنظیم‌تر است. Tradeoff: کمی پیچیدگی بیشتر در ازای انعطاف بیشتر — اگر واقعاً به گام دلخواه نیاز ندارید، نسخه‌ی ساده بهتر است (کد را ساده نگه دارید تا وقتی نیاز واقعی پیدا شود).

تمرین ۴ – مقایسه و تحلیل

۱۳.

برنامه‌نویس رویه‌ایطراح شیءگرا
سوال اول«چه مراحلی باید اجرا شود؟»«چه موجودیت‌هایی درگیرند؟»
واحد فکرتابع و مرحلهشیء و مسئولیت
جای دادهمتغیرهای جدا، پاس‌دادن دستیدرون همان شیء مسئول

(دو ردیف دیگر فصل: «در برابر تغییر» → رویه‌ای باید مراحل را دنبال و دستکاری کند، شیءگرا موجودیت مربوطه را عوض می‌کند؛ «نتیجه در پروژه‌ی بزرگ» → رویه‌ای به درهم‌تنیدگی می‌رسد، شیءگرا به ماژول‌های مستقل.)

۱۴. این توزیع درست است چون هر دانش، جایی زندگی می‌کند که مالکش است:

  • «آیا این نسخه آزاد است؟» دانشی است که مال خود Copy است (نسخه می‌داند دست کسی هست یا نه) → is_available در Copy.
  • «آیا این عضو ظرفیت دارد؟» دانشی است که مال Member است → can_borrow در Member.
  • «امانت‌دادن» یک هماهنگی بین چند موجودیت است: باید از نسخه و عضو سوال بپرسد و اگر همه‌چیز درست بود ثبت کند → borrow در Library.

اگر همه را در یک متد بزرگ Library.borrow می‌ریختیم، Library به داده‌ی داخلی همه دست می‌زد و تبدیل به یک God Object می‌شد. آن‌وقت اگر فردا قانون «سقف امانت» تغییر می‌کرد، باید در دل Library دنبالش می‌گشتیم؛ اما در طرح درست، فقط Member.can_borrow را عوض می‌کنیم و بس. توزیع مسئولیت، تغییر را محلی و امن می‌کند.

تمرین ۵ – پرسش تحلیلی

۱۵. این جمله بیش‌ازحد ساده‌انگارانه است. استراتژی اسم/فعل فقط یک نقطه‌ی شروع برای ساختن فهرست کاندیداهاست، نه یک قاعده‌ی خودکار. اگر هر اسم را کلاس کنیم:

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

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

۱۶. منظور این است که طراحی خوب یک‌بار و کامل از ذهن بیرون نمی‌آید؛ در طول کارکردن با مسئله کشف می‌شود. اولین طرح شما تقریباً همیشه ناقص است، و این کاملاً طبیعی است. طراحی شیءگرا یک فرایند تکرارشونده است: می‌سازید، به مشکل می‌خورید، بازآرایی (Refactor) می‌کنید، بهتر می‌شود.

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

Please login to bookmark Close
نظرات

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

فهرست مطالب

سرفصل دوره

تمرین

این قسمت تمرین ندارد!

پاسخ تمرین ها

هنوز برای تمرین‌های این قسمت پاسخی ثبت نشده است!

اشتراک گذاری

چرا بهتره از فیلترشکن استفاده کنید؟

من همه ویدئو ها و پادکست های کُدباز رو توی یوتیوب و ساندکلود و پلتفرم هایی آپلود می‌کنم که اغلب فیلتر هستند.

اغلب آموزش‌ها ویدئو و پادکست دارند. پس اگر می‌خواهید از محتوای سایت بیشترین استفاده رو ببرید نیاز به فیلتر شکن دارید.

توجه داشته باشید که برای خرید از فروشگاه بهتره فیلتر شکن رو خاموش کنید.

تنظیمات

انتخاب زبان
تغییر تم