الگویِ طراحی Command (جاوا و کاتلین)

فرض کنید رفتید رستوران ، به گارسون سفارشتون رو می‌دید ، گارسون سفارش رو می‌بره تحویل آشپزخونه و بر حسب سفارشتون یکی آماده‌اش می‌کنه ، اگه کباب بخواید ، کباب زن براتون کباب می‌ذاره رو منقل و اگه پیتزا بخواید یک نفر دیگه اون مسئول پخت پیتزا با فِر میشه ، الگویِ طراحی Command دقیقا همینه !

این الگو جزو طبقه‌بندی Behavioral ها حساب میشه چون به رفتار‌های الگوریتم و ارتباط اجزا با هم کار داره ، ما در این الگو چند بخش داریم :

Command

یک interface که شامل تعریفِ توابع اصلی هستند ، مثلا تابع execute ، یا تابع undo برای اینکه Command ای که اجرا کردید رو به حالت قبل برگردونید (یا هر تابعی که شما به نظرتون نیازه مثل redo و ... ، یا فرضا دارید برای کپی پیست یک Command درست می‌کنید و اونا رو هم می‌تونید لحاظ کنید ، من در کدی که در این مقاله میارم فقط execute و undo رو مثال می‌زنم)

ConcreteCommand

معنی Concrete میشه "بتن" ، پس این کلاس بدنه یک Command هست ، یعنی ما بر اساس Command این کلاس رو می‌سازیم و توابعی که در Command داشتیم رو بر حسب این کلاس پیاده سازی می‌کنیم ، چرا میگم "بر حسب این کلاس" ؟ چون می‌تونیم انواع مختلف از این کلاس رو داشته باشیم (در مثال متوجه می‌شید)

Invoker

کلاسی که Command رو به درخواست مورد نظر می‌رسونه ، مثلا یک ریموت کنترل میشه یک Invoker

Receiver

درخواست کننده اون Command ، یعنی کسی که در انتها Command رو دریافت می‌کنه و متناظر اون یک تغییری در خودش می‌ده

صورت مساله

برنامه‌ای می‌نویسیم که بتواند رنگِ کلاسی به اسم Light را بر حسب Command به سه نور قرمز ، آبی و سفید تغییر دهد ، برای این کار اول از همه همه‌ی توابع مورد نیاز برای یک Command رو در یک Interface تعریف می‌کنیم :

https://gist.github.com/sasssass/174e3b579b7304c9cb4cefc0b15db732

حالا نیاز داریم که کلاس Receiver رو که همون Light میشه بسازیم ، این کلاس یک color در خودش داره و تعدادی تابع برای تغییر رنگِ color :

https://gist.github.com/sasssass/852940f20af61bccbd57afb2424b83f9

حالا باید ConcreteCommand ها رو بسازیم ، من در این مثال سه ConcreteCommand می‌سازم برای هر رنگ و این سه کلاس رو از کلاسی به اسم LightCommandMother مشتق می‌کنم ، LightCommandMother یک کلاس abstact هست که بدنه تابع undo در اون پیاده شده و یک Receiver هم به عنوان ورودی می‌گیره ، این کلاس یک اِلِمان به عنوان lastStatus داره که رنگِ حالت قبلی رو در خودش داره (البته برای مثال‌های پیچیده نباید این طوری بنویسید و به جاش می‌تونید از شئ یک clone بسازید و در تابع undo کلِ اون شئ رو درون شئ جدید بریزید) ، باقی ConcreteCommand ها از این کلاس مشتق میشن و تابع execute درون اونها پیاده میشه :

https://gist.github.com/sasssass/a8e1146c80aefa74f694e6b14284a2d9

حالا نیاز به یک Invoker داریم (invoke در لغت به معنی فراخوانی هست) که اسمشو می‌ذاریم RemoteControl ، در این کلاس یک Stack (پشته) از Commandها داریم و یک تابع pressButton ، در ورودیِ pressButton یک Command قرار می‌گیره که اون رو execute کرده و درون stack قرار می‌ده ، در این کلاس تابع undoButton عمل undo رو انجام می‌ده ، اگر stack خالی بود که هیچی وگرنه آخرین Command رو pop می‌کنه و تابع undo اونو صدا می‌زنه :

https://gist.github.com/sasssass/fbd9e1584eca9a1f5b8dc67a4cab25ef

و درآخرم نیاز به کدِ Client هست که بیاد از چیزایی که تا الان تعریف کردیم استفاده کنه ، مثلا چندبار دکمه فشار بده و یکبار هم undo کنه :

https://gist.github.com/sasssass/066dfdaab038d0b6685c73d7e745d3e2

و تمام !

حالا چه زمان‌هایی باید از این الگو استفاده کنیم ؟

  1. زمانی که به سابقه و history از درخواست ها نیاز داریم (مثلا با stack)
  2. زمانی که نیاز داریم درخواست کننده‌ی Command اطلاعی در مورد پیاده‌سازی نداشته باشه (یعنی کد‌ها جدا باشن و پیاده‌سازی‌ها مخفی شده باشند)
  3. لایه‌بندی ایجاد شده در این الگو موجب میشه بعدا اگه لازم شد بهتر و راحت‌تر و دستِ باز تر تغییرات مورد نیازمون رو در کد انجام بدیم
  4. و ...

باقی مقالات در مورد الگوی‌های طراحی رو در این مقاله بخونید .

من رو در لینکدین و اینستاگرام دنبال کنید ???

اگه دوست داشتید می‌تونید به صفحه Spotify بنده هم برید و موسیقی های منو گوش بدید ???