خب قبلا چند مورد از الگوهای طراحی رو بررسی کرده بودیم و حالا وقت اینه که ادامه بدیم و چنتا دیگه ازونا رو باهم بررسی کنیم. ایندفعه نوبت الگوی Template method رسیده.
قبل از پرداختن به این الگو، بیاید مسأله رو بررسی کنیم. فرض کنید ما میخوایم یه سرویسی برای middleware توی سیستم داشته باشیم. توی این سرویس اجرا شدن middleware ها یه الگوریتم خاصی داره و از stepهای مشخصی تشکیل شده. حالا ما مثلا میخوایم به subModuleها اجازه بدیم یه step ازین الگوریتم رو اونطوری که خودشون میخوان پیادهسازی بکنن. چرا همچین کاری میکنیم؟ چون ممکنه ماژولهای متفاوتی با middlewareهای متفاوت داشته باشیم. یا مثلا یکسری از routeهای ما middleware خاص خودشون رو داشته باشن.
خب راه اول اینه که برای هر ماژول بیایم middleware رو طبق الگوریتمی که درنظر گرفتیم پیادهسازی کنیم و فقط step مربوط به خودش رو customize کنیم.
ولی این کار خیلی غلطه. چون به شدت duplicate code رو زیاد میکنه. از طرفی دیگه ممکنه پسفردا یکی دیگه بیاید و یه middleware جدید با یه ساختار دیگه تعریف کنه که ما اینو دوست نداریم. خب چاره چیه؟ استفاده از template method
کاری که میکنیم اینه که یه کلاس middleware تعریف میکنیم و همونطور که از اسم این الگو مشخصه، یک template method داخل این کلاس تعریف میکنیم.
توی این متد، stepهای الگوریتمی که برای middleware درنظر گرفتیم رو مشخص میکنیم. اینجا من میخوام فقط اجازه بدم که step سوم یعنی متد doExecuteMiddleware رو خود subModuleها اونطوری که خودشون میخوان پیاده کنن. بنابراین این متد رو abstract تعریف میکنم و subModuleهارو مجبور میکنم که اون رو پیادهسازی بکنن. بنابراین خود کلاس هم باید abstract باشه و این به این معنیه که ما از خود این کلاس نمیتونیم instance بسازیم. شاید هنوز یکم گنگ باشه صبر کنید بریم جلوتر و یه نگاهی به subModuleها بندازیم.
خب میبینیم که subModuleهای Middleware1 و Middleware2 از superClass ما ارثبری کردن و متد doExecuteMiddleware رو به دلخواه پیادهسازی کردن. اگر دقت کنید میبینید که Middleware2 اومده متد fetchSomeData رو هم override کرده. این رو هم خودمون اجازه دادیم. ما این متد رو توی superClass به صورت protected تعریف کردیم و به subModuleها گفتیم که ما توی step دوم این متد رو صدا میزنیم و یه پیادهسازی پیشفرض هم خودمون براش داریم. حالا شما اگر خواستید این پیادهسازی رو میتونید override کنید و تغییر بدید. در مقابل، step اول الگوریتم ما که initMiddleware باشه بصورت private تعریف شده و هیچ کس نمیتونه تغییرش بده. با استفاده از این الگو و متدهایی که تعریف کردیم، این اطمینان به دست میاد که هم همهی subModuleها، middlewareی که خودشون میخوان رو پیادهسازی میکنن، هم کسی ساختار الگوریتمی که ما برای middlewareها تعریف کردیم رو به هم نمیزنه. زیباست نه؟ :)
حالا بریم سمت client ببینیم چه خبره.
اینجا دیگه دستمون بازه. هم میتونیم یک middleware خاص رو اجرا کنیم و هم اینکه تمام subModuleها رو جمع کنیم و همشون رو instantiate کنیم. دقت کنید اون instanceی که client میسازه، فقط به متد executeMiddleware که همون template method ما باشه دسترسی داره و درستشم همینه. client نباید به ریز متدها و stepهای مختلف الگوریتم ما دسترسی داشته باشه چون این متدها concernش نیستند. بخاطر همین به غیر از متد executeMiddleware، بقیه متدها private و protected هستن.
رابطه Template Method و SOLID
کاری که که ما توی این این الگو انجام دادیم، در حقیقت یکی از اصول SOLID هست. Open to Extension But Closed to Modification. این اصل بیان میکنه که entityهای سیستم شما مثل کلاسها و ماژولها باید اجازه بدن که رفتارشون extend بشه اما اجازه ندن تغییر بکنه. یعنی چی؟ توی همین مثال خودمون، ما اومدیم توی کلاس Middleware با تعریف template method، درواقع یک Extension Point معرفی کردیم و گفتیم که آقا شماها میتونید با extend کردن این کلاس، قسمتی از رفتارش رو اونطوری که خودتون میخواید پیادهسازی کنید. از یک طرف template method ما و مراحل الگوریتم ما همیشه ثابته و قسمتهایی از اون هم به هیچ عنوان overrdiable نیست، بنابراین در عین حالی که جلوی تغییر دادن کلاس Middleware رو گرفتیم، اومدیم به بقیه اجازه دادیم که اون رو extend کنن و اون رفتاری که خودشون میخوان رو توی الگوریتم ما و در اون stepی که ما مشخص کردیم قرار بدن. توجه کنید که template method یک الگوی behavioral یا رفتاری هستش.
اگر فیدبک، انتقاد، پیشنهاد یا هرچیز دیگه ای مدنظرتون هست خیلی ممنون میشم ازتون که برام کامنت کنین یا بهم پیام بدین
LinkedIn: https://www.linkedin.com/in/sajjad-rahimi-890851231/
Telegram: @sajjrhm