پوریا سیفی
پوریا سیفی
خواندن ۶ دقیقه·۲ سال پیش

دیزاین پترن Template Method به زبان ساده ?


به نظر من فقط زمانی مفهوم یک دیزاین پترن برای ما جا میفته که تو محصول واقعی ازش استفاده بکنیم.
از طرفی موقع کد زدن تو محیط واقعی و عملی که میشه کاملا سردرگم هستیم و نمیتونیم شرایط به وجود اومده رو با چه دیزاین پترنی مطابقت بدیم، مگر اینکه بتونیم از مثال هایی که از قبل تو ذهن مون پردازش کردیم استفاده کنیم و شباهت هایی رو کشف کنیم. تو این سری مقالات قصد دارم در مورد دیزاین پترن های مهم مثال هایی رو مطرح کنم که راحت تر بفهمیم در چه شرایطی قابل استفاده و کارا هستند.
برای اولین مورد پترن Template Method رو مناسب دیدم.

تو این مقاله سعی میشه در ابتدا یک مثال ساده تو دنیای واقعی مطرح بشه و در نهایت به مثال های عملی تر در محیط های واقعی بپردازیم.

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

خب تو قدم اول برنامه نویسی رو کنار بذاریم و یک مثال تو دنیای واقعی بزنیم :

فرض کنید یه مغازه ساندویچ فروشی داریم و قراره همبرگر و هات داگ بفروشیم. پس کار اصلی ما درست کردن ساندویچ هست.

درست کردن ساندویچ شامل مراحل میشه :

  • ۱ - برداشتن نون باگت ?
  • ۲- گذاشتن مواد اولیه (همبرگر یا هات داگ ) ?
  • ۳ - گذاشتن گوجه و خیارشور و کاهو ?
  • ۴ - ریختن سس روی ساندیچ ?

خب فرض کنیم مواد اولیه رو خریداری کردیم . قاعدتا شما برای هر نوع ساندویچی نون باگت ها ،گوجه ،خیارشور، کاهو و سس رو یک جای مشترک میذارید.

سوال : آیا منطقی هست که شما بگید این خیارشور و گوجه و کاهو و ... برای همبرگره، این یکی گوجه و کاهو ها برای هات داگ؟

قطعا جواب این سوال نه هست ! بنابراین برای مراحل ۱ و ۳ و ۴ ما یک کار مشخص و مشترک انجام میدیم .

حالا همین مثال ساده رو ببریم تو کد زیر :

<?php abstract class SandwichClass { // template method final public function make(): void { $this->stepA(); $this->stepB(); $this->stepC(); $this->stepD(); } protected function stepA(): void { echo &quotبرداشتن نون باگت&quot } abstract protected function stepB(): void protected function stepC(): void { echo &quotگذاشتن گوجه و خیارشور و کاهو&quot } protected function stepD(): void { echo &quotریختن سس روی ساندیچ&quot } }

خب توی کدی که مشاهده میکنید مراحل مختلف درست کردن ساندویچ آورده شده. مرحله اول و سوم و چهارم که مشترک بود رو توی کلاس abstract گذاشتیم و تو یه کلاس که قراره همه ساندویچ ها ازش استفاده کنند هندل کردیم. قرارمون هم همین بود (کارای تکراری و مشترک).

میمونه همبرگر یا هات داگ بودن ساندوچ، که مشترک نیستند ولی حتما باید مطمئن باشیم مرحله دو هم انجام میشه .

همونطور که میدونید متد های از نوع abstract حتما باید توسط کلاس های زیرین پیاده سازی بشن. بنابراین ما کلاس هایی که از این کلاس ساندویچ ارث بری میکنند رو مجبور کردیم این متد stepB رو پیاده سازی کنند (نمیشه که ساندویچ خالی بدیم دست مردم)

حالا میتونیم برای همبرگر و هات داگ هم دو تا کلاس متفاوت بنویسیم که کارشون فقط درست کردن همون ساندویچ هست (اینجا اصل اول سالید هم داریم رعایت میکنیم که هر کلاسی وظیفه ش یه چیز باشه)

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

class HamburgerClass extends SandwichClass { protected function stepB(): void { echo &quotدرست کردن همبرگر&quot } }


class HotdogClass extends SandwichClass { protected function stepB(): void { echo &quotدرست کردن هات داگ&quot } }


آیا منطقی بود که هر کدوم ازین کلاس ها اون سه مرحله مشترک دیگه رو پیاده سازی کنند و کد تکراری تولید کنیم؟ قطعا خیر.

حالا اگر ما بخواهیم یه ساندویچ جدید هم بسازیم مثلا فلافل، فقط کافیه که یک کلاس فلافل بسازیم و تغییری تو کد های قبلی ایجاد نمیشه (اینم اصل دوم سالید)

در واقع منطق اصلی کار ما (همون چهار تا مرحله) فقط یک جا هندل میشه.

توی این پترن، ما حتما یک کلاس Abstract داریم که یک متد مهم (template method) داره . اسم این دیزاین پترن هم به خاطر همین متد template method نام گذاری شده.

کار متد تمپلت (که تو مثال ما اسم این متد make هست) اینه که مراحل اصلی الگوریتم رو به ترتیب خاصی اجرا میکنه . اگر دقت کنید نوع این متد final هم هست. دلیلشم این هست که کلاس های زیرین اجازه ندارن این متد رو تغییر بدن.

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

تعریف اصلی دیزاین پترن تمپلت :

The Template Method design pattern is a behavioral pattern that defines the skeleton of an algorithm in a superclass but lets subclasses override specific steps of the algorithm without changing its structure.

با توجه به این تعریف و مثال خودمون اگه ساندویچ درست کردن یه الگوریتم باشه، ما مراحلش رو توی کلاس ساندویچ (superclass) اجرا کردیم و اجازه دادیم کلاس های همبرگر و هات داگ (subclasses) متد مرحله دو رو override کنند.

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

مثال های عملی :

مثال یک : ساختن درگاه بانکی که انواع مختلفی از درگاه ها رو ساپورت میکنه :

تو برنامه ی ما فرآیند پرداخت یک الگوریتم و منطق مشترک داره. میتونیم برای هر بانکی یه کلاس بنویسیم و بانک های مختلف باید از الگوریتم ما تبعیت کنند. مثلا همه کاربرا باید

۱ - درخواست خرید ثبت کنند

۲ - به یک درگاه پرداخت متصل بشن و بعد از فرآیند پرداخت ریدایرکت بشن به برنامه ما

۳ - پرداخت شون تایید بشه

مثال دو : ساختن یک اکسپورت که انواع مختلفی از خروجی ها رو داره :

الگوریتم ساخته شدن اکسپورت (super class) انواع مختلفی از اکسپورت ها مثل پی دی اف ، اکسل و ... (sub classes)


مثال سه : ساختن یک ناتیفیکیشن که با اشکال مختلفی ارسال میشه :

الگوریتم ساخته شدن ناتیفیکیشن یک کلاس abstract هست که منطق مشترکی داره (super class) و انواع مختلفی از ناتیفیکیشن میتونه ساخته بشه مثل ایمیل، پیامک، تلگرام و ... (sub classes)


مثال چهار : فرآیند احراز هویت با استفاده از شبکه های اجتماعی دیگر :

فرآیند احراز هویت یک منطق مشترک (super class) داره اما ورود و ثبت نام با گوگل ، گیت هاب ، توئیتر میتونه پیاده سازی های مختلفی داشته باشه (sub classes)


تو همه این مثال ها منطق اصلی یک روال مشخص داره که کلاس های مختلف میتونن برخی از مراحل رو تغییر بدن و مجبورن اون روال رو پیاده سازی کنند.

امیدوارم این مقاله مفهوم رو به خوبی رسونده باشه و به دردتون بخوره



دیزاین پترناصول solid
شاید از این پست‌ها خوشتان بیاید