Az szoftverfejlesztésben a Parancs (Command) minta egy objektumorientált viselkedési minta, melynél egy adott fogadó (Receiver) objektumra történő, a paraméterekkel és minden egyéb függőséggel együtt megadott metódushívást (akció, Action) egy parancs (Command) objektumba zárunk (későbbi meghívás céljából). A parancs objektum tartalmaz egy egyszerű, általában paraméter nélküli publikus metódust (például execute), ennek hívásakor kerül végrehajtásra az akció. A parancs objektum így átadható egy hívó (Invoker) objektumnak, melynek csak az a feladata, hogy (akár szolgaian, akár valamilyen stratégia szerint) végrehajtsa. A parancsokat általában egy vagy több ügyfél (Client) objektum állítja össze és adogatja át a hívónak.
A parancs minta megkönnyíti a generikus komponensek létrehozását. Az ügyfélnek nem kell tudnia arról, hogy a hívó milyen stratégiával és kiegészítésekkel kezeli a végrehajtásokat. A hívónak pedig nem kell ismernie a parancsban lévő hívás részleteit.
Felhasználása A parancs objektumok az implementáláshoz hasznosak.
GUI gombok és menü elemek Swing és Borland Delphi programozási nyelvekben, az „Action” (művelet) egy parancs objektum. Amellett, hogy képes végrehajtani a kívánt parancsot, egy „Action” lehet egy társított ikon, egy billentyűzet gyorsbillentyű, tooltip, és így tovább. Egy eszköztár gomb, vagy egy menüpont összetevője létrejöhet kizárólag egy „Action” vezérlése végett.
Makró felvétel Ha minden felhasználói műveletet (Action) egy parancs objektum reprezentál, akkor a program képes rögzíteni egy összetett művelet lépéseit (Macro) azáltal, hogy egy listába gyűjti a végrehajtott parancs objektumokat. Később ezeket az eredeti sorrendben végighívva újrajátszhatja a műveletsort.
Ha a program "Script" motort is tartalmaz, minden parancs objektum implementálhat egy toScript() metódust, és a felhasználói műveletek akkor könnyebben rögzítve lehetnek "Script"-ként.
Mobil kódolás Olyan nyelveket használva, mint például Java, ahol a kód "Stream"-elhető egy bizonyos távoli helyről egy másikra "URLClassloader"-ek és "Codebase"-k segítségével, ott a parancsok lehetővé tesznek egy újfajta viselkedéseket, a távoli helyeken. (EJB Command, Master Worker)
Többszintű visszavonás A parancs objektum egy gyakori bővített változata, amikor a normál akció mellett egy visszavonó (undo) akciót is tárol, melynek meghívására az állapot visszaáll a normál akció meghívása előttire. Előfordulhat, hogy ez az undo akció késleltetett kiértékelésű, főleg ha csak a művelet végrehajtása után állapítható meg, hogy hogyan kell visszavonni (például random vagy a fogadó állapotától függő műveleteknél). Ha minden felhasználói művelet ilyen típusú parancs objektumként van implementálva, és ezeket (vagy legalább a legutóbbiakat) egy veremben tárolja a program, akkor sorra vissza is lehet őket vonni. Amikor a felhasználó a visszavonást kezdeményezi, lekérésre kerül a veremből a legutóbbi parancs objektum, amelyen rögtön végre kell hajtani az `undo` metódust. Ennek sikeres végrehajtása után a parancs kikerül a veremből (illetve opcionálisan átkerül egy redo verembe).
Networking Lehetőség van rengeteg parancs objektum küldésére a hálózaton keresztül, hogy végrehajtsuk azt egy másik számítógépen.
Párhuzamos feldolgozás Itt a parancsok egy-egy feladatként (task) vannak megírva egy megosztott erőforrás részére, és potenciálisan több szálon, párhuzamosan lesznek végrehajtva.
Folyamatjelzők Feltételezzük, hogy egy program parancsok soraiból épül fel melyek sorban hajtódnak végre. Parancs objektumonként van egy getEstimatedDuration() metódusunk, mellyel, a program könnyen megbecsüli annak teljes hosszát (időtartamát). Ez tükröződhet egy folyamatjelző által, mely észszerűen mutatja, hogy milyen közel van a program ahhoz, hogy végezzen az összes feladattal.
Thread pools (Szálcsoportok) Egy tipikus, általános célú szálcsoport osztálynak rendelkeznie kell egy publikus addTask() metódussal, amely hozzáad egy munkaeszközt egy belső feladat sorához (queue-hoz) várván a feladat befejezésére. Ez azt jelenti, hogy a szálcsoport meghívja a parancsokat a (queue-ból) sorból. A sor elemei parancs objektumok. Általában ezek az objektumok egy közös interfészt (common interface-t) implementálnak, mint például „java.lang.Runnable” amely lehetőséget nyújt a szálcsoportoknak, hogy végrehajtsák a parancsokat még akkor is, ha maga a szálcsoport osztály úgy van megírva, hogy nincs ismerete a konkrét feladatokról.
Tranzakciós viselkedés Hasonlóan a visszavonáshoz, egy adatbázis motor, vagy egy szoftver telepítő tartalmazhat egy listát a műveletekről, amely végre lettek, vagy végre lesznek hajtva. Amennyiben valamelyikük nem sikerül, az összes többi visszafordítható vagy eldobható (általában „rollback”-nek hívják). Például, ha van két adatbázis tábla, melyek hivatkoznak egymásra, amiket frissíteni kell, és a második frissítés nem sikerül, a tranzakció visszaállítható, így az első tábla nem tartalmaz majd hibás hivatkozásokat.
Wizards (Varázslók) Gyakran egy varázsló több oldalnyi konfigurációs lapot mutat be egyetlen művelet érdekében, mely csak akkor következik be, ha a felhasználó rákattint a „Kész” gombra az utolsó oldalon. Ezekben az esetekben a természetes út a felhasználói interfész kódjának és az alkalmazás kódjának szétválasztása érdekében, hogy implementáljuk a varázslót parancs objektum használatával. A parancs objektum akkor jön létre, amikor a varázsló először megjelenik. A varázsló minden oldalában tárolja a GUI változásokat a parancs objektumban, tehát az objektum annyira előrehaladott (belakott), amennyire a felhasználó halad. A „Kész” gomb egyszerűen elindít egy „execute()” hívást. Ezen a ponton kezdődik a parancs osztály munkája, a parancsok sorozatának végrehajtása.
Terminológia
A terminológiát arra használjuk, hogy leírjunk, a parancs tervezési minta implementációk nem következetesek és ennél fogva zavaró lehet. Ez a kétértelműség eredménye, a szinonimák és implementációk elfedhetik az eredeti mintát.
1. Kétértelműség A „command” (parancs) kifejezés kétértelmű. Például a „move up” parancs. A „move up” utalhat egy egyszeri „single” parancsra, amelyet kétszer is végrehajthatunk, vagy utalhat két parancsra, melyek mindegyike ugyan azt tudja. Ha a korábbi parancs kétszer van hozzáadva a „vissza verembe”, akkor mindkét elem a veremben ugyan arra a parancs példányra utal/mutat. Ez megfelelő lehet abban az esetben, ha a parancsot vissza lehet vonni mindig ugyan abba az irányba. Mind a „Gang of Four” és a „Java” példa lejjebb használ interpretációt a parancs kifejezésben. Másfelől, ha az utóbbi parancsok hozzá vannak adva a „vissza verembe”, akkor a verem két különálló objektumra mutat. Ez megfelelő lehet abban az esetben, ha a veremben, példányonként tartalmazzák azon információkat, melyek engedélyezik a parancs visszavonását. A(z) „Execute” (végrehajt) kifejezés is kétértelmű. Ez arra utal, hogy futtasd a kódot a parancs objektumok végrehajtási metódusai által. Habár a Windows Presentation Foundationben (WPF), egy parancs végrehajtottnak tekinthető, ha a parancsok végrehajtói metódusára hivatkozva van, de ez nem feltétlenül azt jelenti, hogy az alkalmazás kódja fut.
2. Szinonimák és homonimák Kliens, forrás, felhasználó: a gomb, eszköztár gomb, vagy a menü elemek kattintása, a gyorsbillentyű gombok lenyomása a felhasználó által. Parancs objektum, Irányított parancs objektum, Művelet objektum: Egy „singleton” objektum, mely birtokában van a gyorsbillentyű gomboknak, a képi gomboknak, a parancs szövegeknek, stb. kapcsolódnak a parancshoz. A parancs/művelet objektumok értesítik a megfelelő forrás/felhasználó objektumokat, amikor parancs/művelet objektumok elérhetősége megváltozik. Ez engedélyezi a gomboknak és a menü elemeknek, hogy inaktívvá váljanak (beszürküljenek) amikor egy parancs/művelet nem hajtható végre. Vevő (fogadó), Cél objektum: az objektum amely a másolásért, beillesztésért, áthelyezésért felel. A vevő objektum birtokolja a metódust, amit parancsok végrehajtói metódusnak (command’s execute method) is neveznek. A vevő tipikusan a cél objektum is egyben. Például, ha egy vevő objektum egy kurzor és a metódus egy „moveUp” nevű metódus, akkor elvárható, hogy a kurzor a moveUp művelet célját képezi. Másfelől, ha a kód a parancs objektum által van definiálva, akkor a cél objektum teljesen más objektum lesz. Parancs objektum, irányított esemény argumentumok, esemény objektumok: az objektum , amely el van választva a forrásból a parancs/műveleti objektumok, a cél objektumok, és a kód felé, amelyek végzik a munkát. Minden gomb kattintás, vagy gyorsbillentyű gomb eredménye egy új parancs/esemény objektum. Néhány implementáció több információt nyújt a parancs/esemény objektumnak. Egyéb implementációk parancs/esemény objektumokat tesznek egyéb esemény objektumba (Mint egy doboz, amiben egy még nagyobb doboz van) Kezelő (Handler), ExecutedRoutedEventHandler, metódus, funkció (function): az aktuális kód amely már elvégzi a másolást, beillesztést, mozgatást, stb. Néhány implementációban a kezelő kód része a parancs/műveleti objektumnak. Más implementációkban a kód része a vevő/cél objektumoknak, és még további más implementációkban a kezelő kód külön van választva más egyéb objektumoktól. Parancs menedzser, vissza menedzser, időzítő, sor (queue), diszpécser, felhasználó (invoker): egy objektum mely a parancs/esemény objektumot beleteszi egy „vissza verembe” (undo stack), vagy egy „helyrehoz verembe” (redo stack), vagy addig tartja a parancs/esemény objektumot, amíg egyéb objektumok késszé akarják tenni őket, vagy irányítják a parancs/esemény objektumot a megfelelő vevő/cél objektum felé vagy kezelő kódhoz.
3. Implementációk, melyek már túlmutatnak az eredeti parancs tervezési mintán A Windows Presentation Foundation (WPF) bemutatta az irányított parancsokat, melyek kombinálják a parancs tervezési mintát az esemény feldolgozással. Ennek eredményeképpen a parancs objektum többé már nem tartalmaz hivatkozást a cél objektum felé, és az applikáció kódja felé sem. Ehelyett felhasználja a parancs objektumok meghívási parancsainak eredményeit egy úgynevezett „Executed Routed Event” – Végrehajtott irányított eseményt, amely a „Tunneling” vagy „Bubbling” események alatt fordulhat elő egy úgynevezett „Binding” objektumban, amely azonosítja a célt és az applikáció kódot, amely már el van végezve ezen a ponton.
Példák Tekintsünk egy egyszerű kapcsolót, legyen a neve „Switch”. Ebben a példában konfigurálunk egy "Switch"-et két paranccsal: ez a kettő nem más mint, „kapcsoljuk LE” a lámpát, és „kapcsoljuk FEL” a lámpát.
Az előnye ennek a különös parancs tervezési minta implementációnak az az, hogy a "Switch" használható bármilyen eszköz által, nem csak a lámpa által. A "switch" a következő példában le és felkapcsolhatja a lámpát, de a "Switch"-ek konstruktora képes elfogadni bármilyen más "Subclass" parancsát aminek két paramétere van. Például úgy is be tudjuk állítani a "Swith"-et, hogy az ki vagy be kapcsoljon egy motort.
C# A következő kód a „Parancs tervezési minta” egy implementációja C#-ban. using System; using System.Collections.Generic;
namespace CommandPattern { public interface ICommand
void Execute();
/* The Invoker class */ public class Switch
ICommand _closedCommand; ICommand _openedCommand;
public Switch(ICommand closedCommand, ICommand openedCommand)
_closedCommand = closedCommand; _openedCommand = openedCommand;
//close the circuit/power on public void Close()
_closedCommand.Execute();
//open the circuit/power off public void Open()
_openedCommand.Execute();
/* An interface that defines actions that the receiver can perform */ public interface ISwitchable
void PowerOn(); void PowerOff();
/* The Receiver class */ public class Light : ISwitchable
public void PowerOn()
Console.WriteLine("The light is on");
public void PowerOff()
Console.WriteLine("The light is off");