Uzorak dizajna strategije u Javi 8

1. Uvod

U ovom ćemo članku pogledati kako možemo implementirati obrazac dizajna strategije u Javi 8.

Prvo ćemo dati pregled uzorka i objasniti kako se to tradicionalno primjenjuje u starijim verzijama Jave.

Zatim ćemo ponovno isprobati obrazac, samo ovaj put s lambdama Java 8, smanjujući opširnost našeg koda.

2. Strateški obrazac

U osnovi, strateški obrazac omogućuje nam promjenu ponašanja algoritma tijekom izvođenja.

Tipično bismo započeli sa sučeljem koje se koristi za primjenu algoritma, a zatim ga implementirali više puta za svaki mogući algoritam.

Recimo da imamo zahtjev primijeniti različite vrste popusta na kupnju, ovisno o tome je li to Božić, Uskrs ili Nova godina. Prvo, izradimo a Diskont sučelje koje će implementirati svaka naša strategija:

javno sučelje Discounter {BigDecimal applyDiscount (BigDecimal iznos); } 

Onda recimo da želimo primijeniti popust od 50% za Uskrs i 10% za Božić. Primijenimo naše sučelje za svaku od ovih strategija:

javna statička klasa EasterDiscounter implementira Discounter {@Override public BigDecimal applyDiscount (konačni BigDecimalni iznos) {return iznos.multiply (BigDecimal.valueOf (0.5)); }} javna statička klasa ChristmasDiscounter implementira Discounter {@Override public BigDecimal applyDiscount (konačni BigDecimalni iznos) {return iznos.multiply (BigDecimal.valueOf (0.9)); }} 

Na kraju, isprobajmo strategiju u testu:

Discounter easterDiscounter = novi UskrsDiscounter (); BigDecimal discountValue = easterDiscounter .applyDiscount (BigDecimal.valueOf (100)); assertThat (discountValue) .isEqualByComparingTo (BigDecimal.valueOf (50));

To djeluje prilično dobro, ali problem je što može biti malo muke morati stvoriti konkretnu klasu za svaku strategiju. Alternativa bi bila uporaba anonimnih unutarnjih tipova, ali to je i dalje prilično opširno i ne puno korisnije od prethodnog rješenja:

Discounter easterDiscounter = novi Discounter () {@Preuzmi javni BigDecimal applyDiscount (konačni BigDecimalni iznos) {povratni iznos.multiply (BigDecimal.valueOf (0.5)); }}; 

3. Iskorištavanje Java 8

Otkako je objavljena Java 8, uvođenje lambda učinilo je anonimne unutarnje tipove više-manje suvišnim. To znači da je stvaranje strategija u skladu sada puno čišće i jednostavnije.

Nadalje, deklarativni stil funkcionalnog programiranja omogućuje nam provođenje obrazaca koji prije nisu bili mogući.

3.1. Smanjivanje iscrpnosti koda

Pokušajmo stvoriti inline Uskršnji diskont, samo ovaj put pomoću lambda izraza:

Discounter easterDiscounter = iznos -> iznos.multiply (BigDecimal.valueOf (0.5)); 

Kao što vidimo, naš je kod sada puno čišći i održiviji, postižući isto kao i prije, ali u jednom retku. U srži, lambda se može vidjeti kao zamjena za anonimni unutarnji tip.

Ova prednost postaje očiglednija kada želimo izjaviti još više Popusti u redu:

Navedi diskontore = newArrayList (iznos -> iznos.množiti (BigDecimal.valueOf (0,9)), iznos -> iznos.multiply (BigDecimal.valueOf (0,8)), iznos -> iznos.multiply (BigDecimal.valueOf (0,5))) ;

Kad želimo definirati puno Popusti, možemo ih statički prijaviti na jednom mjestu. Java 8 čak nam omogućuje definiranje statičkih metoda u sučeljima ako to želimo.

Dakle, umjesto da biramo između konkretnih klasa ili anonimnih unutarnjih tipova, pokušajmo stvoriti lambde u jednom razredu:

javno sučelje Discounter {BigDecimal applyDiscount (BigDecimal iznos); static Discounter christmasDiscounter () {povratni iznos -> iznos.multiply (BigDecimal.valueOf (0.9)); } static Discounter newYearDiscounter () {povratni iznos -> iznos.multiply (BigDecimal.valueOf (0.8)); } static Discounter easterDiscounter () {povratni iznos -> iznos.multiply (BigDecimal.valueOf (0.5)); }} 

Kao što vidimo, postižemo puno u ne baš mnogo koda.

3.2. Sastav funkcije iskorištavanja

Izmijenimo svoj Diskont sučelje pa proširuje UnaryOperator sučelje, a zatim dodajte a kombinirati() metoda:

javno sučelje Discounter proširuje UnaryOperator {zadani kombinirač Discounter (Discounter nakon) {return value -> after.apply (this.apply (value)); }}

U osnovi, mi prepravljamo svoje Diskont i iskorištavanje činjenice da je primjena popusta funkcija koja pretvara a BigDecimal primjer u drugu BigDecimal primjer, omogućujući nam pristup unaprijed definiranim metodama. Kao UnaryOperator dolazi s primijeniti () metodu, možemo samo zamijeniti applyDiscount s tim.

The kombinirati() metoda je samo apstrakcija oko primjene jedne Diskont do rezultata ovaj. Koristi ugrađenu funkcionalnost primijeniti () kako bi se to postiglo.

Pokušajmo primijeniti višestruku primjenu Popusti kumulativno na iznos. To ćemo učiniti pomoću funkcije smanjiti() i naše kombinirati():

Discounter kombiniraniDiscounter = diskontori .stream () .reduce (v -> v, Discounter :: kombiniraj); combDiscounter.apply (...);

Obratite posebnu pozornost na prvu smanjiti argument. Kad popusti nisu predviđeni, moramo vratiti nepromijenjenu vrijednost. To se može postići pružanjem funkcije identiteta kao zadanog diskontera.

Ovo je korisna i manje opširna alternativa izvođenju standardne iteracije. Ako uzmemo u obzir metode koje izvlačimo iz okvira za funkcionalni sastav, to nam također daje puno više funkcionalnosti besplatno.

4. Zaključak

U ovom smo članku objasnili strateški obrazac, a također smo pokazali kako možemo koristiti lambda izraze kako bismo ga primijenili na način koji je manje opširan.

Implementacija ovih primjera može se naći na GitHubu. Ovo je projekt zasnovan na Mavenu, pa bi ga trebalo biti lako pokrenuti kakav jest.