Command design pattern

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
GoF

Представяме си, че имаме една проста програма, например текстов редактор. И той нека има меню с няколко бутончета – New, Open, Save, Save all и т.н…, чиято работа ще е не директно да свършат това, за което за били кликнати, а само да извикат това, което трябва да стане, когато са кликнати, което си е в текст редактора (нека го наречем „бизнес моделът„).

Тоест, идеята е да организираме викането на функционалността в основният обект, не разпределянето и в подкласове.

Единият начин да организираме работата на тези бутончета (нека ги наречем „викачи“) е например да имаме абсклас/интерфейс Button и всяко бутонче да екстендва/имплементира със свой клас. И всеки от тях да вика съответната функционалност в главният обект – „бизнес моделът„.

Това не е фатално въпреки, че може да доведе до експлозия от класове когато имаме нужда от повече и повече бутончета. И стига да внимаваме да не счупим всичко щом променим родителят Button.

Отделно, да не забравяме, че дадена функционалност може да трябва да се вика не само при клик на бутонче, а и например при събития като Ctrl+N, Ctrl+S…
И за всяко от тях ли ще правим клас-викач?

Command pattern казва, всяка от тези отделни команди, като New, Open, Save… трябва да бъдат капсулирани в класове – клас NewCommand, OpenCommand, SaveCommand…
И отделните бутончета или събития (Ctrl+N…), тоест „викачи“, да викат тях, а не директно да викат функционалността на „бизнес модела“.

Тоест, принципът например за Save конкретно, да НЕ Е този

а този

Което ще рече, че този design pattern е подходящ когато трябва да можем по-унифицирано и гъвкаво да можем да викаме функционалност (методи) от даден обект, от повече от един „викач“. Демек, не отделните „викачи“ сами да си викат това, което трябва да викат, а да викат един общ викач, който да вика това, което трябва.

В крайна сметка, дали ще кликна Save дискетката, ще натисна Ctrl+S или ще дам с десен бутон Save контекстното меню, не става ли все едно и също?

Отделно, като добавим такъв „медиатор“, първо евентуални промени по начина на викане на „бизнес модела“ ще стават на едно място (капсулация).

Също и отделните „викачи“ (бутончета, клавишни комбинации, контекстни менюта…) нямат задължението и нуждата да знаят нещо за „бизнес модела“ (изолация).

Eдно възможно предимство на този design pattern е, че например, щом сме капсулирали отделните команди в общ абсклас, можем да пазим например отделните състояния, да имаме например history, което например би ни помогнало да направим нещо като undo функционалност.

Литература:

https://refactoring.guru/design-patterns/command

https://sourcemaking.com/design_patterns/command

https://sourcemaking.com/design_patterns/command/php