Strategický návrhový vzor v prostredí Java 8

1. Úvod

V tomto článku sa pozrieme na to, ako môžeme implementovať vzor návrhu stratégie v prostredí Java 8.

Najskôr poskytneme prehľad vzoru a vysvetlíme, ako sa tradične implementuje v starších verziách Javy.

Ďalej tento vzor vyskúšame znova, tentokrát však s lambdas Java 8, čím sa zníži výrečnosť nášho kódu.

2. Strategický vzor

Strategický vzor nám v zásade umožňuje meniť správanie sa algoritmu za behu programu.

Typicky by sme začali s rozhraním, ktoré sa používa na použitie algoritmu, a potom ho implementujeme viackrát pre každý možný algoritmus.

Povedzme, že máme požiadavku na nákup rôznych druhov zliav podľa toho, či sú Vianoce, Veľká noc alebo Nový rok. Najskôr vytvorme a Zľavňovač rozhranie, ktoré bude implementované každou z našich stratégií:

verejné rozhranie Zľava {BigDecimal applyDiscount (suma BigDecimal); } 

Potom si povedzme, že chceme uplatniť 50% zľavu na Veľkú noc a 10% zľavu na Vianoce. Implementujme naše rozhranie pre každú z týchto stratégií:

verejná statická trieda EasterDiscounter implementuje zľavu {@Override public BigDecimal applyDiscount (konečná suma BigDecimal) {return amount.multiply (BigDecimal.valueOf (0,5)); }} public static class ChristmasDiscounter implements Discounter {@Override public BigDecimal applyDiscount (final BigDecimal amount) {return amount.multiply (BigDecimal.valueOf (0.9)); }} 

Nakoniec vyskúšame stratégiu v teste:

Diskontný veľkonočný diskontér = nový Veľkonočný diskontný program (); BigDecimal discountedValue = easterDiscounter .applyDiscount (BigDecimal.valueOf (100)); assertThat (discountedValue) .isEqualByComparingTo (BigDecimal.valueOf (50));

To funguje celkom dobre, ale problém je v tom, že môže byť trochu nepríjemné vytvoriť konkrétnu triedu pre každú stratégiu. Alternatívou by bolo použitie anonymných vnútorných typov, ale je to stále dosť podrobné a nie oveľa šikovnejšie ako predchádzajúce riešenie:

Diskontný easterDiscounter = nový Diskontný () {@Override verejný BigDecimal applyDiscount (konečná veľkáDecimálna suma) {návratná čiastka.multiply (BigDecimal.valueOf (0,5)); }}; 

3. Využitie Java 8

Od vydania Java 8 zavedením lambdas boli anonymné vnútorné typy viac-menej nadbytočné. To znamená, že vytváranie stratégií v súlade je teraz oveľa čistejšie a jednoduchšie.

Deklaratívny štýl funkčného programovania nám navyše umožňuje implementovať vzory, ktoré predtým neboli možné.

3.1. Znižovanie výrečnosti kódu

Skúsme vytvoriť vložený riadok Veľkonočná zľava, iba tentokrát pomocou výrazu lambda:

Diskontný veľkonočný diskontér = suma -> suma.multiply (BigDecimal.valueOf (0,5)); 

Ako vidíme, náš kód je teraz oveľa čistejší a udržiavateľnejší, dosahuje rovnaké výsledky ako predtým, ale v jednom riadku. V podstate lambda sa môže považovať za náhradu anonymného vnútorného typu.

Táto výhoda sa stáva zreteľnejšou, keď chceme deklarovať ešte viac Zľavy v rade:

Zoznam diskontérov = newArrayList (množstvo -> množstvo.multiply (BigDecimal.valueOf (0,9)), množstvo -> množstvo.multiply (BigDecimal.valueOf (0,8)), množstvo -> množstvo.multiply (BigDecimal.valueOf (0,5))) ;

Keď chceme definovať veľa Zľavy, môžeme ich staticky deklarovať všetky na jednom mieste. Java 8 nám umožňuje, ak chceme, dokonca definovať statické metódy v rozhraniach.

Namiesto toho, aby sme si vybrali medzi konkrétnymi triedami alebo anonymnými vnútornými typmi, skúsme vytvoriť lambdy všetko v jednej triede:

verejné rozhranie Zľava {BigDecimal applyDiscount (suma BigDecimal); statický Diskontér christmasDiscounter () {návratná suma -> suma.multiply (BigDecimal.valueOf (0,9)); } statický diskontér newYearDiscounter () {návratná suma -> suma.multiply (BigDecimal.valueOf (0,8)); } statický diskontér easterDiscounter () {návratná suma -> suma.multiply (BigDecimal.valueOf (0,5)); }} 

Ako vidíme, v nie veľmi veľa kódu dosahujeme veľa.

3.2. Zloženie funkcie pákového efektu

Upravme naše Zľavňovač rozhranie tak rozširuje UnaryOperator rozhranie a potom pridajte a kombinovať () metóda:

verejné rozhranie Discounter rozširuje UnaryOperator {predvolené Discounter combine (Discounter after) {návratová hodnota -> after.apply (this.apply (value)); }}

V zásade refaktorujeme naše Zľavňovač a využitie skutočnosti, že uplatnenie zľavy je funkcia, ktorá prevádza a BigDecimal napríklad do iného BigDecimal inštancia, čo nám umožňuje prístup k preddefinovaným metódam. Ako UnaryOperator prichádza s použiť () metódu, môžeme len nahradiť applyDiscount s tým.

The kombinovať () metóda je iba abstrakcia okolo použitia jednej Zľavňovač k výsledkom toto. Využíva zabudovanú funkčnosť použiť () aby sa to dosiahlo.

Teraz skúsme použiť viac Zľavy kumulatívne na sumu. Urobíme to pomocou funkcionálu znížiť () a náš kombinovať ():

Diskontér kombinovanýDiskontér = diskonty .stream () .reduce (v -> v, Diskontér :: kombinovať); combinedDiscounter.apply (...);

Venujte zvláštnu pozornosť prvému zmenšiť argument. Ak nie sú poskytované žiadne zľavy, musíme vrátiť nezmenenú hodnotu. To sa dá dosiahnuť poskytnutím funkcie identity ako predvoleného diskontéra.

Toto je užitočná a menej podrobná alternatíva k vykonaniu štandardnej iterácie. Ak vezmeme do úvahy metódy, ktoré dostávame z krabice pre funkčné zloženie, dáva nám tiež oveľa viac funkcií zadarmo.

4. Záver

V tomto článku sme vysvetlili vzor stratégie a tiež sme demonštrovali, ako môžeme pomocou výrazov lambda implementovať tento spôsob menej verbálne.

Implementáciu týchto príkladov možno nájsť na GitHub. Toto je projekt založený na Maven, takže by mal byť ľahko spustiteľný tak, ako je.


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