A parancsminta Java-ban
1. Áttekintés
A parancsminta egy viselkedési tervezési minta, és része a GoF hivatalos tervezési mintázatának. Egyszerűen fogalmazva, a minta szándéka objektumba foglalja az adott művelet (parancs) végrehajtásához szükséges összes adatot, beleértve azt, hogy melyik metódust hívjuk meg, a metódus argumentumait és az objektumot, amelyhez a metódus tartozik.
Ez a modell lehetővé teszi számunkra szétválasztani azokat az objektumokat, amelyek a parancsokat előállítják a fogyasztóktól, ezért a mintát közismert nevén termelői-fogyasztói mintának nevezik.
Ebben az oktatóanyagban megtudhatjuk, hogyan lehet a parancsmintát Java-ban megvalósítani mind objektum-orientált, mind objektum-funkcionális megközelítések segítségével, és meglátjuk, milyen felhasználási esetekben lehet hasznos.
2. Objektum-orientált megvalósítás
Klasszikus megvalósításban a parancsminta megköveteli négy komponens megvalósítása: a Command, a Receiver, a Invoker és az Client.
Készítsünk egy alap példát, hogy megértsük a minta működését és az egyes komponensek szerepét.
Tegyük fel, hogy szöveges fájl alkalmazást szeretnénk kifejleszteni. Ilyen esetben meg kell valósítanunk a szöveges fájlokkal kapcsolatos műveletek végrehajtásához szükséges összes funkciót, például megnyitni, írni, szöveges fájlt menteni stb.
Tehát le kell bontanunk az alkalmazást a fent említett négy komponensre.
2.1. Parancsosztályok
A parancs egy olyan objektum, amelynek szerepe a tárolja a művelet végrehajtásához szükséges összes információt, beleértve a hívás módját, a metódus argumentumokat és a metódust megvalósító objektumot (vevő néven).
Annak érdekében, hogy pontosabb képet kapjunk a parancsobjektumok működéséről, kezdjünk el fejleszteni egy egyszerű parancsréteget, amely csak egyetlen interfészt és két megvalósítást tartalmaz:
@FunctionalInterface nyilvános felület TextFileOperation {String execute (); }
public class Az OpenTextFileOperation megvalósítja a TextFileOperation {privát TextFile textFile; // konstruktorok @Orride public String execute () {return textFile.open (); }}
public class SaveTextFileOperation implementálja a TextFileOperation {// ugyanezt a mezőt és konstruktort, mint a fentiekben. }}
Ebben az esetben a TextFileOperation Az interfész meghatározza a parancsobjektumok API-ját és a két megvalósítást, OpenTextFileOperation és SaveTextFileOperation, hajtsa végre a konkrét intézkedéseket. Az előbbi megnyit egy szöveges fájlt, míg az utóbbi egy szöveges fájlt.
Világos látni a parancsobjektum funkcionalitását: a TextFileOperation parancsokat összesíti az összes szükséges információt egy szöveges fájl megnyitásához és mentéséhez, beleértve a vevő objektumot, a hívás módszereit és az argumentumokat (ebben az esetben nincs szükség argumentumra, de lehet).
Érdemes ezt hangsúlyozni a fájlműveleteket végrehajtó komponens a vevő (a Szöveges fájl példa).
2.2. A vevő osztály
A vevő olyan tárgy, amely összetartó cselekvések halmazát hajtja végre. Ez az a komponens, amely végrehajtja a tényleges műveletet, amikor a parancs van végrehajtani () módszert nevezzük.
Ebben az esetben meg kell határoznunk egy vevőosztályt, amelynek szerepe a modellezés Szöveges fájl tárgyak:
public class TextFile {private String name; // konstruktor public String open () {return "Megnyitó fájl" + név; } public String save () {return "Fájl mentése" + név; } // további szövegfájl-módszerek (szerkesztés, írás, másolás, beillesztés)}
2.3. Az Invoker osztály
Az invoker olyan tárgy, amely tudja, hogyan kell végrehajtani egy adott parancsot, de nem tudja, hogyan hajtották végre a parancsot. Csak a parancs felületét ismeri.
Bizonyos esetekben a meghívó tárolja és sorba állítja a parancsokat, eltekintve azok végrehajtásától. Ez hasznos néhány további szolgáltatás, például a makrórögzítés vagy a visszavonás és újragyártás funkcióinak megvalósításához.
Példánkban nyilvánvalóvá válik, hogy a parancsobjektumok meghívásáért és a parancsokon keresztül történő végrehajtásáért további összetevőnek kell lennie. végrehajtani () módszer. Pontosan itt játszik szerepet az invoker osztály.
Nézzük meg invokerünk alapvető megvalósítását:
public class TextFileOperationExecutor {private final List textFileOperations = new ArrayList (); public String executeOperation (TextFileOperation textFileOperation) {textFileOperations.add (textFileOperation); return textFileOperation.execute (); }}
A TextFileOperationExecutor osztály csak egy vékony absztrakciós réteg, amely elválasztja a parancsobjektumokat a fogyasztóiktól és meghívja a metódust a TextFileOperation parancs objektumok.
Ebben az esetben az osztály a parancsobjektumokat az a-ban is tárolja Lista. Természetesen ez nem kötelező a minta megvalósításában, hacsak nem kell további kontrollt adnunk a műveletek végrehajtási folyamatához.
2.4. Az ügyfélosztály
Az ügyfél egy olyan objektum, amely vezérli a parancs végrehajtásának folyamatát megadva, hogy mely parancsokat kell végrehajtani, és a folyamat mely szakaszaiban kell végrehajtani őket.
Tehát, ha ortodox akarunk lenni a minta formális meghatározásával, akkor a tipikus használatával létre kell hoznunk egy ügyfélosztályt fő- módszer:
public static void main (String [] args) {TextFileOperationExecutor textFileOperationExecutor = new TextFileOperationExecutor (); textFileOperationExecutor.executeOperation (új OpenTextFileOperation (új TextFile ("file1.txt")))); textFileOperationExecutor.executeOperation (új SaveTextFileOperation (új TextFile ("file2.txt")))); }
3. Objektum-funkcionális megvalósítás
Eddig objektum-orientált megközelítést alkalmaztunk a parancsminta megvalósításához, ami nagyon jó.
A Java 8-tól kezdve használhatunk objektum-funkcionális megközelítést, lambda-kifejezéseken és metódus-hivatkozásokon alapulva tegye egy kicsit kompaktabbá és kevésbé bőbeszédűvé a kódot.
3.1. A Lambda kifejezések használata
Mivel a TextFileOperation interfész funkcionális interfész, tudjuk adja át a parancsobjektumokat lambda kifejezések formájában az invokernek, anélkül, hogy létre kellene hozni a TextFileOperation példányok kifejezetten:
TextFileOperationExecutor textFileOperationExecutor = új TextFileOperationExecutor (); textFileOperationExecutor.executeOperation (() -> "File1.txt fájl megnyitása"); textFileOperationExecutor.executeOperation (() -> "File1.txt fájl mentése");
A megvalósítás most sokkal korszerűbbnek és tömörebbnek tűnik, mint mi csökkentette a kazánlap kódjának mennyiségét.
Ennek ellenére továbbra is fennáll a kérdés: jobb ez a megközelítés az objektumorientálthoz képest?
Nos, ez trükkös. Ha feltételezzük, hogy a kompaktabb kód a legtöbb esetben jobb kódot jelent, akkor valóban az.
Alapszabályként felhasználásonként kell értékelnünk, hogy mikor kell lambda kifejezéseket igénybe venni.
3.2. A Method References használata
Ehhez hasonlóan használhatunk metódus-hivatkozásokat is parancsobjektumok átadása a meghívónak:
TextFileOperationExecutor textFileOperationExecutor = új TextFileOperationExecutor (); TextFile textFile = új TextFile ("file1.txt"); textFileOperationExecutor.executeOperation (textFile :: open); textFileOperationExecutor.executeOperation (textFile :: save);
Ebben az esetben a megvalósítás az egy kicsit bőbeszédűbb, mint a lambdákat használó, mivel még létre kellett hoznunk a Szöveges fájl példányok.
4. Következtetés
Ebben a cikkben megismertük a parancsminta kulcsfontosságú fogalmait és azt, hogy miként valósíthatjuk meg a mintát a Java-ban egy objektum-orientált megközelítés, valamint a lambda-kifejezések és a módszerreferenciák kombinációjával.
Szokás szerint az ebben az oktatóanyagban bemutatott összes kódpélda elérhető a GitHubon.