نوشتن متد فکتوری در پایتون | به زبان ساده


اگر تا کنون «الگوهای طراحی» (Design Patterns) را مطالعه کرده باشید، احتمالاً با یک الگوی کلاسیک به نام متد «فکتوری» (Factory) آشنا هستید. این الگو موجب میشود که فرزندان یک کلاس مبنا اشیایی که میتوانند بسازند را تعیین کنند و به این ترتیب موجب ایجاد انعطافپذیری بیشتری میشود. به این ترتیب به جای این که نام اشیا را به صورت هاردکدشده درون کلاس والد بیاوریم، آنها را در اختیار فرزندان قرار میدهیم. در این مقاله با روش نوشتن متد فکتوری در پایتون آشنا خواهیم شد.
این الگو موجب بسط آسانتر کلاس میشود و ما را مطمئن میسازد که اشیای با «تزویج سست» (Loosely Coupled) به دست میآوریم. برای نمونه فرض کنید یک کلاس والد به نام Bicycle داریم و میخواهیم Bicycle یک مجموعه از تایرها داشته باشد. بنابراین هر بار که یک Bicycle جدید وهلهسازی میشود، به صورت خودکار tires را به GenericTires نسبت میدهیم. تا اینجا همه چیز روشن است، اما اگر بخواهیم نوع متفاوتی از Bicycle با نوع متفاوتی از tires داشته باشیم چطور؟ یا اگر اینترفیس Bicycle تغییر یابد و یا بخواهیم آن را به کلی حذف کنیم؟
در این موارد باید کلاس Bicycle والد نیز بهروزرسانی شود. الزام به مراجعه مجدد به کد و بهروزرسانی عناصر هاردکدشده مانند این کاری دشوار، غیر منعطف و مستعد بروز خطا است. هدف استفاده از متد فکتوری این است که بسطپذیری بیشتری به اشیا بدهیم و آنها را تجرید کنیم، به طوری که استفاده از آنها آسانتر باشد. این کار قاعده سادهای به صورت زیر دارد:
اشیا را در عملیات مجزایی ایجاد کنید تا زیرکلاس بتواند روش ایجاد آنها را Override کند.
این دقیقاً همان کاری است که قصد داریم در پایتون انجام دهیم.
تنظیم اولیه
در مثال نخست قصد داریم با نمونه کدهایی آشنا شویم که به طور معمول مستعد استفاده از متد فکتوری هستند. بدین منظور از مثال مناسب «دوچرخه» (Bicycle) که در بخش قبلی دیدیم استفاده خواهیم کرد.
توجه کنید که وقتی چیزهای فیزیکی مانند دوچرخه که برایمان آشنا است را مدلسازی میکنیم، مفاهیم بهتر تفهیم میشود.
در این مثال، کلاس والد Bicycle با یک مجموعه از GenericTires و از طریق ارجاع مستقیم به کلاس مقداردهی میشد. این امر موجب میشود که تزویج بین Bicycle و GenericTires هر چه محکمتر شود. ایجاد انواع متفاوتی از دوچرخه اینک ممکن است، اما تغییر دادن تایرهای آنها در این مثال کار سرراستی نیست.
فرض کنید میخواهیم یک نوع جدیدی از دوچرخه مثلاً یک دوچرخه کوهستان بسازیم. در این صورت احتمالاً به نوع خاصی از تایر نیاز خواهیم داشت. اما چطور میتوانیم کلاس مبنای Bicycle را به صورتی انعطافپذیرتر برای استفاده از انواع خاصی از تایر دربیاوریم؟ در ادامه مثالی از یک طراحی میبینید که انعطافَپذیری آن کمی بیشتر است:
در مثال فوق، یک متد جدید به نام ()add_tires برای بازگشت دادن کلاس تایر صحیح، با استفاده از Return در پایتون اضافه کردهایم. بدین ترتیب میتوانیم این متد را در هر یک از زیرکلاسهای Bicycle باطل سازیم و کلاس دیگری از تایرها را ارائه کنیم. بنابراین به منظور افزودن یک دوچرخه جدید و تایرهای متفاوت تنها کافی است کلاسهای جدید را اضافه کرده و متد را Override کنیم. در ادامه به بررسی یک مثال دیگر میپردازیم که این مفهوم را بیشتر بسط میدهد تا بتوان اجزای پیچیدهتری از دوچرخه را نیز تغییر داد.
یک طراحی با انعطافپذیری بیشتر
در این بخش مثالی از یک طراحی با انعطافپذیری بیشتر را میبینیم که مشابه الگوی فکتوری مجرد است. در این مثال یک پارامتر factory به کلاس والد اضافه میشود که نوعی از part factory برای تعیین نوع تایرها یا دیگر اجزایی که باید ساخته شوند، میپذیرد.
در این مثال ما دیگر نوع اجزایی که باید در کلاس والد نصب شوند را به هیچ وجه تعیین نمیکنیم. افزودن اجزا از سوی متدهای متناظر ()add_tires و ()add_frame درون part factory انجام میگیرد. این part factory در واقع یک کلاس مجزا است که وظیفه افزودن هر بخش مجزا را مدیریت میکند. کلاس والد Bicycle این متدها را برای افزودن اجزای درون کلاس part factory فرامیخواند. هر وهله همچنان یک Bicycle است، اما با اجزای مختلفی ساخته شده است.
همچنین برخی مراحل دیگر وجود دارند که میتوان برای تجرید مجموعه قبلی از کلاسها به میزان بیشتر از این نیز استفاده شود:
- تعمیم ()add_tires و ()add_frame به add_part('type')
- برای نمایش همه اجزای مرتبط با دوچرخه میتوانیم متدی در کلاس والد Bicycle ایجاد کنیم. شما میتوانید این کار را به سادگی با ایجاد هر ()part_type یا با جابجا کردن self.tires و self.frame به درون یک لیست یا map از اجزا و تعریف حلقهای روی آن انجام دهید.
- هماینک self.tires و self.frame انتظار دارند که یک part factory را دریافت کنند. شما میتوانید GenericFactory را درون کلاس به صورت انتسابهای پیشفرض قرار دهید که میتوانند از سوی factory-های جدید override شوند.
سخن پایانی
این مثالها تنها برخی از روشهای نوشتن و بسط الگوهای Factory را در پایتون نمایش میدهند.
چندین نسخه دیگر نیز وجود دارند. این که کدام یک از این روشها را انتخاب میکنید به طور کامل به اندازه پروژه و پیچیدگی کلاس وابسته است. در برخی موارد فقط لازم است که یک متد را در کلاس والد Override کنید، اما در پارهای دیگر از موارد باید یک کلاس Factory اختصاصی برای وهلهسازی چند شیء بسازید. در نهایت هر گزینهای که انتخاب کنید، باید یک اینترفیس ساده، سرراست و بسطپذیر در اختیار کلاینتها قرار دهید.