الگوی طراحی راهحل نیست، اسمِ کاری است که زبان شما خودش انجام نمیدهد. برای همین فهرستشان در هر زبان فرق میکند و در پایتون کوتاهتر است. یک فایل از کد یک کتابفروشی اینترنتی در رشت را باز کنید و سهتایشان از قبل آنجاست، هرکدام یک سطر. اولی این است:
sefareshha = [("R-114", 3, 900), ("R-115", 1, 2400), ("R-116", 2, 1200)]def bar_asas_vazn(s): return s[2]sorted(sefareshha, key=bar_asas_vazn)که R-114 را پیش از R-116 و آن را پیش از R-115 میگذارد
استراتژی یک تابع است که از بیرون داده میشود و در محل فراخوانی انتخاب میشود. bar_asas_vazn را با bar_asas_mahal عوض کنید و رفتار مرتبسازی عوض میشود بیآنکه sorted یک حرف تغییر کند. در زبانی که تابع، مقدار نیست، همین چیدمان به یک رابط، یک کلاس برای هر ترتیب و یک شی که از بیرون داده میشود نیاز دارد، و الگو دقیقاً برای همان نسخه اسم گرفت. sorted(sefareshha, key=bar_asas_vazn) همان الگوی استراتژی است که از قبل در زبان هست.
دومی دکوراتور است که پایتون آن را با علامت اتساین مینویسد. دکوراتور تابعی میگیرد و تابعی با همان امضا برمیگرداند: @zamansanj بالای def nerkh(vazn) محاسبه کرایه را داخل یک زمانسنج میپیچد و هیچکدام از فراخوانیکنندههای nerkh دست نمیخورند. سومی تکنمونه بود و با هیچ کاری به دست آمد: یک ماژول پایتون خودش تکنمونه است، یک بار وارد میشود و در sys.modules میماند، پس import nerkhha در چهار فایل، چهار ارجاع به یک شی میدهد.
دوتایی که زبان به شما نمیدهد
ناظر فهرستی از توابعی است که موضوع، هنگام تغییر، صدایشان میزند. نه سطر، و کتابفروشی لازمش دارد چون تغییر وضعیت یک سفارش هم باید پیامک بفرستد و هم موجودی انبار را کم کند، و ماه دیگر باید یک تابلوی داخل انبار را هم بهروز کند:
def dar_taghir(self, fn): self.nazerha.append(fn)def vaziat(self, v):و بعدfor fn in self.nazerha: fn(self.kod, v)
حالا کلاس سفارش هیچ چیز درباره پیامک و تابلوی انبار نمیداند. اضافه کردن ناظر سوم در ماه بعد یک سطر در کد انبار است و صفر سطر در کلاس سفارش، و کل بازدهی آن نه سطر همین است.
دومی مبدل است. سرویس قدیمی پست به mahal_bastebandi(kod) جواب میدهد و هر چیزی که تا امروز نوشته شده peygiri(kod) صدا میزند. مبدل یک شی را میپیچد تا به فراخوانیهایی جواب بدهد که بقیه کد از قبل میکند: یک کلاس که سرویس قدیمی را نگه میدارد و یک peygiri دارد که داخلش mahal_bastebandi را صدا میزند، و آن پنجاه جایی که منتظر peygiri هستند دستنخورده میمانند. پنج سطر است و ارزانترین چیز این صفحه، چون جایگزینش پنجاه ویرایش و یک تعارض ادغام است.
سه قفسه، و اینکه این تقسیم به چه درد میخورد
اسمها بر اساس اینکه الگو درباره چیست بایگانی شدهاند، نه بر اساس اینکه چطور نوشته میشوند، و دانستن سه قفسه به این درد میخورد که میگوید کمبود شما از کدام نوع است. راههای ساختن شی، که فقط new نیستند درباره این است که چه چیزی ساخته شود و چه کسی تصمیم بگیرد: کارخانه تابعی است که تصمیم میگیرد کدام کلاس ساخته شود تا فراخوانیکننده تصمیم نگیرد، و همان است که کد رشت با آن یک کد روش ارسال را از پایگاه داده به شی پست پیشتاز یا پیک محلی یا تحویل حضوری تبدیل میکند.
کنار هم گذاشتن شیها برای ساختن شکلهای بزرگتر جایی است که مبدل بالا زندگی میکند، همراه هر چیز دیگری که دو چیزِ ساختهنشده برای هم را جفت میکند. و اینکه شیها چطور با هم حرف میزنند استراتژی و ناظر را نگه میدارد، که هر دو جواب یک ناراحتیاند: چیزی باید تغییر کند، و آنچه تغییر میکند نباید به آنچه ثابت میماند سیمکشی شود.
ابزار، و سالی که این حرف درست است
دکوراتوری که بیدقت نوشته شود اسم تابعی را که پیچیده عوض میکند. بدون functools.wraps روی تابع داخلی، بدون functools.wraps مقدار __name__ برابر inner میشود و مستند تابع هم میرود، و این هر پیام خطا و هر ابزاری را که تابع را با نامش گزارش میکند خراب میکند. functools.wraps نام و مستند تابع پیچیدهشده را حفظ میکند، یک سطر خرج دارد، و جای کشف کردن این مسئله از روی ردپای خطایی را گرفت که نام اشتباهی را چاپ میکرد.
دو حرکت دیگر هم بهجای فهرست، در خود زبان هستند. functools.singledispatch، از پایتون 3.4 در سال ۲۰۱۴، پیادهسازی را بر اساس نوع آرگومان اول انتخاب میکند و جای زنجیرهای از بررسیهای isinstance را گرفت که هر بار یک نوع تازه میآمد بلندتر میشد. contextlib.contextmanager تابعی با یک yield را به چیزی تبدیل میکند که با with استفاده میشود، و جای نوشتن کلاسی با __enter__ و __exit__ را گرفت که همان تضمین پاکسازی را میداد. هر دو در کتابخانه استاندارد پایتون 3.14 هستند و از ایران هم برای هیچکدام نصبی لازم نیست.
بایگانی
هر قفسه به چه کار میآید، و کتابفروشی از هرکدام چه برداشت
اسمها بر اساس آنچه تغییر میکند بایگانی شدهاند، و به همین دلیل قفسه به شما میگوید مسئلهتان واقعاً از کدام نوع است.
الگوهای طراحی
- ساختنیچه چیزی ساخته میشود، و چه کسی تصمیم میگیرد
- کارخانهیک کد روش ارسال به پست پیشتاز یا پیک محلی یا تحویل حضوری تبدیل میشود
- تکنمونهیک نمونه، و ماژول از قبل یکی است
- ساختاریجفت کردن دو چیزی که برای هم ساخته نشده بودند
- مبدلpeygiri() جلوی mahal_bastebandi()
- رفتاریاینکه اجزا چطور برای حرف زدن چیده میشوند
- استراتژیترتیبی که به sorted داده میشود
- ناظرفهرست تماشاگرهای یک سفارش
شش اسم، نه کل فهرست. قفسهها دهها اسم دیگر هم دارند، و بیشترشان فقط در زبانها و کدهایی بزرگتر از این مثال خرج خودشان را درمیآورند.
اشتباه: ناظری که موضوع را با خودش پایین میکشد
آن نه سطر ناظر یک ایراد دارد که تا روز قطعی سرویس پیامک دیده نمیشود. حلقه، ناظرها را به ترتیب صدا میزند و هیچکدام را جدا نگه نمیدارد، پس اگر یکی از ناظرها خطا بدهد، ناظرهای بعدی اصلاً صدا زده نمیشوند. سرویس پیامک ConnectionError میدهد، انبار هرگز خبردار نمیشود، و خود vaziat هم با خطا بالا میآید، یعنی سفارشی که واقعاً ارسال شده در سامانه ارسالنشده میماند. یک قطعی بیرونی، سه چیز را با هم برد.
درستش سه سطر است: هر فراخوانی داخل try برود، خطا در جای خودش ثبت شود، و حلقه ادامه پیدا کند. الگو به شما گفت چه چیزی را به چه چیزی وصل کنید و درباره اینکه یک ناظر بد چه حقی دارد چیزی نگفت، و هیچ الگویی هم نمیگوید. این تصمیم مال شماست و همیشه هم مال شما بوده.
جایی که این موضوع در ایران میایستد: بیست و سه اسم برای مصاحبه
الگوها اینجا معمولاً بهشکل فهرستی از بیست و سه اسم وارد میشوند که پیش از مصاحبه حفظ میشوند. آن فهرست در سال ۱۹۹۴ برای زبانی نوشته شد که در آن تابع، مقدار نبود، و کسی هنوز همان بحث را برای زبانهایی که تابع در آنها مقدار است از نو اجرا نکرده است. نتیجهاش آدمی است که هر بیست و سه اسم را میداند، در کد خودش هیچکدام را نمیبیند، و بعد اولین کارخانهاش را روی مسئلهای میسازد که هنوز پیش نیامده.
ارزش واقعی این اسمها جای دیگری است و در تیم فارسیزبان بیشتر هم هست: در بازبینی کد، گفتن «این ناظر است» یک بحث بیست دقیقهای را تمام میکند. به همین دلیل هم اسم را باید فقط روی چیزی گذاشت که واقعاً همان است؛ اسمی که سر جایش نباشد بحث را کوتاه نمیکند، گمراهش میکند.
آزمون سالمش گذشتهنگر است نه آیندهنگر: الگو روزی ارزش اضافه شدن دارد که مورد دوم رسیده باشد، نه روزی که بشود تصورش کرد. اینکه مورد دوم واقعاً رسیده یا نه، قضاوتی درباره کدِ جلوی چشم است نه درباره الگوها، و زیر عادتهایی که تعیین میکنند بعداً کسی میتواند این کد را عوض کند یا نه گرفته میشود. چیدمانهایی هم که این اسمها توصیف میکنند، چیدمان شیها هستند و سه شکل مربوط بودن دو کلاس زیر برنامهنویسی شیگرا است، که خودش زیر ایدههایی که با عوض شدن زبان از بین نمیروند نشسته است.