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.


$config[zx-auto] not found$config[zx-overlay] not found