Uzorak naredbi u Javi

1. Pregled

Uzorak naredbi obrazac je ponašanja i dio je GoF-ovog formalnog popisa uzoraka dizajna. Jednostavno rečeno, obrazac to namjerava enkapsulirati u objekt sve podatke potrebne za izvođenje zadane radnje (naredbe), uključujući koju metodu pozvati, argumente metode i objekt kojem metoda pripada.

Ovaj nam model omogućuje razdvojiti objekte koji proizvode naredbe od svojih potrošača, pa je zato obrazac poznat kao obrazac proizvođač-potrošač.

U ovom uputstvu naučit ćemo kako implementirati obrazac naredbi u Javi koristeći objektno orijentirani i objektno-funkcionalni pristup i vidjet ćemo u kojim slučajevima upotrebe može biti koristan.

2. Objektno orijentirana implementacija

U klasičnoj implementaciji, obrazac naredbe zahtijeva implementirajući četiri komponente: naredbu, prijamnik, pozivatelj i klijent.

Da bismo razumjeli kako obrazac funkcionira i ulogu koju igra svaka komponenta, stvorimo osnovni primjer.

Pretpostavimo da želimo razviti aplikaciju tekstualne datoteke. U tom bismo slučaju trebali implementirati svu funkcionalnost potrebnu za izvođenje nekih operacija povezanih s tekstualnom datotekom, poput otvaranja, pisanja, spremanja tekstualne datoteke i slično.

Dakle, trebali bismo rastaviti aplikaciju na četiri gore spomenute komponente.

2.1. Zapovjedne klase

Naredba je objekt čija je uloga pohraniti sve podatke potrebne za izvršavanje radnje, uključujući metodu koju treba pozvati, argumente metode i objekt (poznat kao primatelj) koji implementira metodu.

Da bismo dobili precizniju ideju o tome kako funkcioniraju naredbeni objekti, započnimo s razvojem jednostavnog naredbenog sloja koji uključuje samo jedno sučelje i dvije implementacije:

@FunctionalInterface javno sučelje TextFileOperation {String execute (); }
javna klasa OpenTextFileOperation implementira TextFileOperation {private TextFile textFile; // konstruktori @Override javni String execute () {return textFile.open (); }}
javna klasa SaveTextFileOperation implementira TextFileOperation {// isto polje i konstruktor kao gore @Override public String execute () {return textFile.save (); }} 

U ovom slučaju, TextFileOperation sučelje definira API objekata naredbi i dvije implementacije, OpenTextFileOperation i SaveTextFileOperation, izvršiti konkretne radnje. Prvi otvara tekstualnu datoteku, dok drugi sprema tekstualnu datoteku.

Jasno je vidjeti funkcionalnost naredbenog objekta: TextFileOperation naredbe uvrstiti sve potrebne podatke za otvaranje i spremanje tekstualne datoteke, uključujući objekt primatelja, metode za pozivanje i argumente (u ovom slučaju nisu potrebni argumenti, ali bi mogli biti).

Vrijedno je to naglasiti komponenta koja izvodi operacije datoteka je prijemnik ( TextFile primjer).

2.2. Razred prijamnika

Prijemnik je objekt koji izvodi skup kohezivnih radnji. To je komponenta koja izvodi stvarnu radnju kada je naredba izvršiti() metoda se naziva.

U ovom slučaju moramo definirati klasu prijamnika čija je uloga modeliranje TextFile objekti:

javna klasa TextFile {naziv privatnog niza; // konstruktor public String open () {return "Otvaranje datoteke" + ime; } javni String spremi () {return "Spremanje datoteke" + ime; } // // dodatne metode tekstualne datoteke (uređivanje, pisanje, kopiranje, lijepljenje)} 

2.3. Razred dozivača

Pozivač je objekt koji zna kako izvršiti zadanu naredbu, ali ne zna kako je naredba implementirana. Zna samo sučelje naredbe.

U nekim slučajevima pozivač također sprema i stavlja naredbe u redove, osim njihova izvršavanja. Ovo je korisno za implementaciju nekih dodatnih značajki, poput makro snimanja ili poništavanja i ponovnog korištenja.

U našem primjeru postaje očito da mora postojati dodatna komponenta odgovorna za pozivanje objekata naredbe i njihovo izvršavanje putem naredbi ' izvršiti() metoda. Upravo tu stupa na snagu klasa pozivatelja.

Pogledajmo osnovnu implementaciju našeg poziva:

javna klasa TextFileOperationExecutor {privatni konačni popis textFileOperations = novi ArrayList (); javni niz executeOperation (TextFileOperation textFileOperation) {textFileOperations.add (textFileOperation); vratiti textFileOperation.execute (); }}

The TextFileOperationExecutor razred je samo a tanak sloj apstrakcije koji razdvaja naredbene objekte od njihovih potrošača i poziva metodu inkapsuliranu unutar TextFileOperation naredbeni objekti.

U ovom slučaju, klasa također pohranjuje naredbene objekte u Popis. Naravno, to nije obvezno u provedbi uzorka, osim ako ne trebamo dodati neku daljnju kontrolu u postupak izvršavanja operacija.

2.4. Klasa klijenta

Klijent je objekt koji kontrolira postupak izvršavanja naredbe navodeći koje naredbe treba izvršiti i u kojim fazama procesa ih izvršiti.

Dakle, ako želimo biti pravoslavni s formalnom definicijom uzorka, moramo stvoriti klasu klijenta koristeći tipičnu glavni metoda:

javna statička void glavna (String [] args) {TextFileOperationExecutor textFileOperationExecutor = novi TextFileOperationExecutor (); textFileOperationExecutor.executeOperation (novi OpenTextFileOperation (novi TextFile ("file1.txt")))); textFileOperationExecutor.executeOperation (novi SaveTextFileOperation (novi TextFile ("file2.txt")))); } 

3. Objektno-funkcionalna implementacija

Do sada smo koristili objektno orijentirani pristup za implementaciju naredbenog uzorka, što je sve u redu.

Iz Jave 8 možemo koristiti objektno-funkcionalni pristup zasnovan na lambda izrazima i referencama na metode kôd učinite malo kompaktnijim i manje opširnim.

3.1. Korištenje Lambda izraza

Kao TextFileOperation sučelje je funkcionalno sučelje, možemo proslijediti pozivne objekte u obliku lambda izraza pozivaču, bez potrebe za stvaranjem TextFileOperation primjerci izričito:

TextFileOperationExecutor textFileOperationExecutor = novi TextFileOperationExecutor (); textFileOperationExecutor.executeOperation (() -> "Otvaranje datoteke file1.txt"); textFileOperationExecutor.executeOperation (() -> "Spremanje datoteke file1.txt"); 

Implementacija sada izgleda mnogo pojednostavljenija i sažetija, kao i mi smanjena količina šifre tipa.

Bez obzira na to, i dalje stoji pitanje: je li ovaj pristup bolji u odnosu na objektno orijentirani?

Pa, to je zeznuto. Ako pretpostavimo da kompaktniji kod u većini slučajeva znači bolji kôd, onda to zaista jest.

Kao osnovno pravilo, trebali bismo procijeniti od slučaja do upotrebe kada se pribjegavati lambda izrazima.

3.2. Korištenje referenci o metodama

Slično tome, možemo koristiti reference metoda za prosljeđivanje objekata naredbe pozivaču:

TextFileOperationExecutor textFileOperationExecutor = novi TextFileOperationExecutor (); TextFile textFile = novi TextFile ("file1.txt"); textFileOperationExecutor.executeOperation (textFile :: open); textFileOperationExecutor.executeOperation (textFile :: save); 

U ovom slučaju, provedba je malo više riječi od one koja koristi lambde, jer smo još uvijek morali stvoriti TextFile instance.

4. Zaključak

U ovom smo članku naučili ključne koncepte naredbenog uzorka i kako implementirati obrazac u Javu pomoću objektno orijentiranog pristupa i kombinacije lambda izraza i referenci na metode.

Kao i obično, svi primjeri koda prikazani u ovom vodiču dostupni su na GitHubu.