Faragiri

همهٔ زبان‌ها

آموزش الگوهای ساختالگوهای طراحی

سه سطر که جای یک زنجیره شرط را می‌گیرند و اضافه کردن قالب تازه را از ویرایش کد به افزودن یک سطر داده تبدیل می‌کنند.

۴بخش
۳دقیقه خواندن
۴عمق

یک پرسش، دو جواب

چه کسی تصمیم می‌گیرد کدام کلاس ساخته شود

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

انتخاب کلاس به داده وابسته است؟
  • خیر
    مستقیم بسازیدیک پیاده‌سازی هست و هر لایه‌ای رویش فقط خواندن را سخت می‌کند
    • CsvReader() و تمام
    • اگر روزی حالت دوم آمد، همان روز عوضش کنید
  • بله
    انتخاب را یک جا جمع کنیدیک نگاشت از داده به کلاس، و همه مصرف‌کننده‌ها از همان می‌پرسند
    • یک دیکشنری از پسوند به کلاس
    • قالب تازه یک سطر داده است

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

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

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

  • KHANANDE = {".csv": CsvReader, ".json": JsonReader}
  • def khanande_baraye(nam):
  • pasvand = os.path.splitext(nam)[1]
  • return KHANANDE[pasvand]()

صدا زدنش با نام foroush.csv یک نمونه CsvReader تحویل می‌دهد. کلاس در پایتون خودش یک مقدار است، پس می‌شود در یک دیکشنری گذاشتش و بعدا صدایش زد، و همین است که کل زنجیره شرط را حذف می‌کند. اضافه کردن یک قالب تازه یک سطر در دیکشنری است و نه یک شاخه تازه در تابع. حالت ناشناخته را صریح جواب دهید، وگرنه KeyError با یک پیام بی‌معنی برای کاربر بالا می‌آید: تبدیلش کنید به خطایی که نام پسوند در آن باشد، مثل khanande baraye .xlsx nist. همان بند from None هم زنجیره را تمیز نگه می‌دارد و خطای داخلی دیکشنری را در گزارش نمی‌آورد.

سه شکل دیگر، و کاری که هرکدام حل می‌کنند

سازنده‌ای که آرگومان‌های زیاد و اختیاری دارد، جایش متد کلاسی است: Faktor.az_radif(satr) و Faktor.az_json(payload) دو راه ساخت‌اند که هر دو همان کلاس را برمی‌گردانند و نامشان می‌گوید ورودی از کجا آمده. این ارزان‌ترین الگوی این صفحه است و بیشترین استفاده را دارد.

شی‌ای که ساختنش گران است و باید یکی باشد، در پایتون معمولا نه با کلاس تک‌نمونه‌ای بلکه با یک ماژول حل می‌شود، چون ماژول یک بار وارد می‌شود و همان یک بار بس است؛ یا با functools.cache روی تابع سازنده. و وقتی ساختن یک شی چند مرحله دارد که ترتیبشان مهم است، الگوی سازنده تدریجی یعنی یک شی جدا که مرحله‌ها را جمع می‌کند و در پایان یک بار می‌سازد.

و جایی که هیچ‌کدام لازم نیست

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

جایی که کار گیر می‌کند، ترجمه مستقیم از جاواست

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

ابزار، و سالی که این حرف درست است

در سال ۲۰۲۶، functools.cache در کتابخانه استاندارد و رایگان است و جای کلاس تک‌نمونه‌ای با متغیر سراسری را می‌گیرد؛ روی یک تابع بدون آرگومان، همان یک شی را برای همیشه برمی‌گرداند و هیچ کلاسی لازم ندارد. برای ساختن نمونه از داده خام، dataclasses در همان کتابخانه سازنده را خودش می‌نویسد و متد کلاسی شما فقط تبدیل ورودی را انجام می‌دهد. هیچ‌کدام بسته بیرونی نیستند.

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

کنار این

بالای این