Dupla feladás DDD-ben
1. Áttekintés
A kettős feladás egy szakkifejezés a a meghívandó módszer kiválasztásának folyamata mind a vevő, mind az argumentumtípusok alapján.
Sok fejlesztő gyakran összekeveri a kettős elküldést a Strategy Patternel.
A Java nem támogatja a kettős elküldést, de vannak olyan technikák, amelyeket alkalmazhatunk ennek a korlátnak a leküzdésére.
Ebben az oktatóanyagban a kettős elosztás példáinak bemutatására összpontosítunk a tartományvezérelt tervezés (DDD) és a stratégiai minta összefüggésében.
2. Dupla feladás
Mielőtt megvitatnánk a kettős elküldést, tekintsünk át néhány alapot, és magyarázzuk el, mi is az az Egyetlen feladás valójában.
2.1. Egyetlen feladás
Az egyszeri feladás lehetővé teszi a vevő futási idején alapuló módszer megvalósításának megválasztását. A Java-ban ez alapvetően ugyanaz, mint a polimorfizmus.
Vessünk egy pillantást például erre az egyszerű kedvezménypolitikai felületre:
nyilvános felület DiscountPolicy {kettős kedvezmény (Megrendelés); }
A DiscountPolicy interfész két megvalósítással rendelkezik. A lapos, amely mindig ugyanazt a kedvezményt adja vissza:
public class FlatDiscountPolicy implementálja a DiscountPolicy {@Orride public double discount (Order order) {return 0.01; }}
És a második megvalósítás, amely visszatérítést ad a megrendelés teljes költsége alapján:
public class AmountBasedDiscountPolicy implementálja a DiscountPolicy {@Orride public double discount (Order order) {if (order.totalCost () .isGreaterThan (Money.of (CurrencyUnit.USD, 500.00)))) {return 0,10; } else {return 0; }}}
Tegyük fel, hogy ennek a példának az igényei szerint a Rendelés osztálynak van egy összköltsége() módszer.
Az egyszeri szállítás a Java-ban csak egy nagyon jól ismert polimorf viselkedés, amelyet a következő teszt mutat be:
@DisplayName ("adott két diszkont házirend," + ", amikor ezeket a házirendeket használja," + ", majd az egyszeri feladás a futásidejű típus alapján választja a megvalósítást") ); DiscountPolicy amountPolicy = új AmountBasedDiscountPolicy (); Order orderWorth501Dollars = orderWorthNDollars (501); // amikor dupla flatDiscount = flatPolicy.discount (orderWorth501Dollars); dupla összegDiszkont = összegPolitika.diszkont (orderWorth501Dollars); // majd assertThat (flatDiscount) .isEqualTo (0,01); assertThat (összegDiszkont) .isEqualTo (0,1); }
Ha mindez elég egyértelműnek tűnik, maradj velünk. Később ugyanezt a példát fogjuk használni.
Most készen állunk a kettős feladás bevezetésére.
2.2. Dupla elküldés vs módszer túlterhelése
A kettős feladás meghatározza a futás közbeni meghívás módját mind a vevő, mind az argumentumtípusok alapján.
A Java nem támogatja a kettős elküldést.
Ne feledje, hogy a kettős küldést gyakran összekeverik a módszer túlterhelésével, ami nem ugyanaz. A módszer túlterhelése csak a fordítási idő információ alapján választja meg a meghívás módját, például a változó deklarációs típusát.
A következő példa részletesen elmagyarázza ezt a viselkedést.
Vezessünk be egy új diszkont felületet SpecialDiscountPolicy:
nyilvános felület A SpecialDiscountPolicy kiterjeszti a DiscountPolicy {kettős kedvezményt (SpecialOrder megrendelés); }
Különleges rendelés egyszerűen kiterjed Rendelés új viselkedés hozzáadása nélkül.
Most, amikor létrehozunk egy példányt Különleges rendelés de nyilvánítsa normálisnak Rendelés, akkor a speciális kedvezményes módszert nem alkalmazzák:
@DisplayName ("adott diszkont politika, amely speciális megrendeléseket fogad el," + ", amikor a rendes megrendelésként deklarált speciális megrendelésekre vonatkozó szabályt alkalmazza," + ", akkor a rendszeres diszkont módszert alkalmazzák") @Test void test () dobja a Kivételt {// adott SpecialDiscountPolicy specialPolicy = new SpecialDiscountPolicy () {@ Nyilvános kettős kedvezmény felülbírálása (Megrendelés) {return 0,01; } @Orride public double discount (SpecialOrder order) {return 0,10; }}; Rendelés specialOrder = új SpecialOrder (anyOrderLines ()); // amikor dupla kedvezmény = specialPolicy.discount (specialOrder); // majd assertThat (kedvezmény) .isEqualTo (0,01); }
Ezért a módszer túlterhelése nem kettős elosztás.
Még akkor is, ha a Java nem támogatja a kettős elküldést, használhatunk egy mintát hasonló viselkedés elérésére: Látogató.
2.3. Látogatóminta
A Látogató minta lehetővé teszi számunkra, hogy új viselkedést adjunk a meglévő osztályokhoz anélkül, hogy azokat módosítanánk. Ez a kettős elküldés okos technikájának köszönhető.
Hagyjuk egy pillanatra a kedvezményes példát, hogy bemutassuk a Visitor mintát.
Képzelje el, hogy HTML nézeteket szeretnénk készíteni különböző sablonok használatával az egyes sorrendekhez. Hozzáadhatnánk ezt a viselkedést közvetlenül a rendelési osztályokhoz, de ez nem a legjobb ötlet az SRP megsértése miatt.
Ehelyett a Visitor mintát fogjuk használni.
Először be kell mutatnunk a Látogatható felület:
nyilvános felület Látható {void accept (V látogató); }
Látogatói felületet is használunk, a mi nevünkben OrderVisitor:
nyilvános felület OrderVisitor {void visit (Rendelési sorrend); érvénytelen látogatás (SpecialOrder megrendelés); }
A Látogató minta egyik hátránya azonban az, hogy a látogatható osztályok megkövetelik a Látogató ismeretét. Ha az osztályokat nem úgy tervezték, hogy támogassák a Látogatót, nehéz lehet (vagy akár lehetetlen is, ha a forráskód nem áll rendelkezésre) ennek a mintának az alkalmazása. Minden rendeléstípusnak meg kell valósítania a Látogatható felületet, és biztosítja a saját megvalósítását, amely látszólag azonos, egy másik hátrány. Vegye figyelembe, hogy a Rendelés és Különleges rendelés azonosak: Csábító lehet, hogy nem hajtja végre újra elfogad az alosztályban. Ha azonban nem tennénk, akkor a OrderVisitor.visit (Rendelés) A módszer a polimorfizmus miatt természetesen mindig megszokott. Végül nézzük meg a OrderVisitor felelős a HTML nézetek létrehozásáért: Rendszeres megrendelés teljes költsége:% s összköltség A következő példa bemutatja a HtmlOrderViewCreator: Rendszeres megrendelés teljes költsége:. * összköltsége: .* Az előző szakaszokban a kettős feladásról és a Látogató mintáról tárgyaltunk. Most végre készen állunk megmutatni, hogyan kell használni ezeket a technikákat a DDD-ben. Térjünk vissza a megrendelésekre és a kedvezménypolitikákra. Korábban bemutattuk a Rendelés osztály és annak összköltsége() módszer, amely kiszámítja az összes rendelési sor összegét: Ott van még a DiscountPolicy felület a megrendelés kedvezményének kiszámításához. Ezt a kezelőfelületet azért hozták létre, hogy lehetővé tegye a különféle kedvezménypolitikák használatát és futás közben történő megváltoztatását. Ez a kialakítás sokkal rugalmasabb, mint egyszerűen az összes lehetséges kedvezményes politika kemény kódolása Rendelés osztályok: Ezt eddig nem említettük kifejezetten, de ez a példa a stratégiai mintát használja. A DDD gyakran használja ezt a mintát, hogy megfeleljen a mindenütt használt nyelv elvének és alacsony csatolást érjen el. A DDD világban a stratégiai mintát gyakran Policy-nek nevezik. Lássuk, hogyan lehet kombinálni a kettős diszpécselési technikát és a kedvezménypolitikát. A házirend-minta megfelelő használatához gyakran érdemes ezt érvként átadni. Ez a megközelítés a Tell, Don't Ask elvét követi, amely támogatja a jobb beágyazást. Például a Rendelés osztály megvalósíthatja összköltsége így: Tegyük fel, hogy az egyes rendeléstípusokat másképp szeretnénk feldolgozni. Például a speciális megrendelések árengedményének kiszámításakor néhány más szabály megköveteli a (z) Különleges rendelés osztály. Kerülni akarjuk a leadást és a reflexiót, és képesek vagyunk mindegyikre kiszámolni a teljes költségeket Rendelés a helyesen alkalmazott kedvezménnyel. Azt már tudjuk, hogy a módszer túlterhelése fordítási időben történik. Tehát felmerül a természetes kérdés: hogyan tudjuk dinamikusan diszponálni a rendelési engedmény logikáját a megfelelő módszerre a megbízás futásideje alapján? A válasz? Kicsit módosítanunk kell a rendelési osztályokat. A gyökér Rendelés osztálynak futás közben el kell küldenie a kedvezménypolitika érvet. Ennek legegyszerűbb módja egy védett hozzáadása ApplyDiscountPolicy módszer: Ennek a kialakításnak köszönhetően elkerüljük az üzleti logika duplikálását a összköltsége módszer ben Rendelés alosztályok. Mutassunk egy használati bemutatót: Ez a példa továbbra is a Visitor mintát használja, de kissé módosított változatban. A rendosztályok tisztában vannak ezzel SpecialDiscountPolicy (a Látogató) rendelkezik valamilyen jelentéssel és kiszámítja a kedvezményt. Amint azt korábban említettük, szeretnénk tudni, hogy a futási idő típusától függően különböző kedvezményes szabályokat alkalmazzunk Rendelés. Ezért felül kell írnunk a védettet ApplyDiscountPolicy módszer minden gyermekosztályban. Írjuk felül ezt a módszert Különleges rendelés osztály: Most már felhasználhatunk további információkat a következőkről: Különleges rendelés a kedvezményes politikában a megfelelő kedvezmény kiszámításához: Ezenkívül, mivel a polimorf viselkedést alkalmazzuk a sorrendosztályokban, könnyen módosíthatjuk a teljes költség számítási módszert. Ebben a cikkben megtanultuk a kettős diszpécsertechnika és Stratégia (más néven Irányelv) minta Domain-alapú tervezésben. Az összes példa teljes forráskódja elérhető a GitHub oldalon.public class Order végrehajtja Visitable {@Override public void accept (OrderVisitor látogató) {visitor.visit (this); }} public class SpecialOrder kiterjeszti a megrendelést {@Override public void accept (OrderVisitor látogató) {visitor.visit (this); }}
public class HtmlOrderViewCreator implementálja a OrderVisitor {private String html; public String getHtml () {return html; } @Orride public void visit (Order order) {html = String.format ("
@DisplayName ("adott rendes és különleges megrendelések gyűjteménye," + ", amikor HTML nézetet hoz létre minden egyes megrendeléshez a látogató segítségével," + ", majd az egyes rendelésekhez létrejön a dedikált nézet") adott Lista anyOrderLines = OrderFixtureUtils.anyOrderLines (); Lista megrendelések = Arrays.asList (új rendelés (anyOrderLines), új SpecialOrder (anyOrderLines)); HtmlOrderViewCreator htmlOrderViewCreator = new HtmlOrderViewCreator (); // amikor megrendelések.get (0) .accept (htmlOrderViewCreator); String regularOrderHtml = htmlOrderViewCreator.getHtml (); megrendelések.get (1) .accept (htmlOrderViewCreator); String specialOrderHtml = htmlOrderViewCreator.getHtml (); // majd assertThat (regularOrderHtml) .containsPattern ("
3. Dupla feladás DDD-ben
3.1. A diszkont politika mint stratégiai minta
public class Megrendelés {public Money totalCost () {// ...}}
nyilvános felület DiscountPolicy {kettős kedvezmény (Megrendelés); }
3.2. Dupla elküldési és engedményezési politika
public class Megrendelés / * ... * / {// ... public Money totalCost (SpecialDiscountPolicy discountPolicy) {return totalCost (). multipliedBy (1 - discountPolicy.discount (this), RoundingMode.HALF_UP); } // ...}
public class Megrendelés / * ... * / {// ... public Money totalCost (SpecialDiscountPolicy discountPolicy) {return totalCost (). multipliedBy (1 - ApplyDiscountPolicy (discountPolicy), RoundingMode.HALF_UP); } védett kettős ApplyDiscountPolicy (SpecialDiscountPolicy discountPolicy) {return discountPolicy.discount (this); } // ...}
@DisplayName ("adott rendszeres megrendelés 100 USD értékű tételekkel," + ", ha 10% -os kedvezménypolitikát alkalmazunk," + ", majd a kedvezmény utáni költség 90 USD") @Test void test () kivételt dob Rendelés (OrderFixtureUtils.orderLineItemsWorthNDollars (100)); SpecialDiscountPolicy discountPolicy = new SpecialDiscountPolicy () {@Orride public double discount (Order order) {return 0,10; } @Orride public double discount (SpecialOrder order) {return 0; }}; // amikor Money totalCostAfterDiscount = order.totalCost (discountPolicy); // majd assertThat (totalCostAfterDiscount) .isEqualTo (Money.of (CurrencyUnit.USD, 90)); }
public class SpecialOrder meghosszabbítja a megrendelést {// ... @Orride védett kettős applyDiscountPolicy (SpecialDiscountPolicy discountPolicy) {return discountPolicy.discount (this); } // ...}
@DisplayName ("adott különleges megrendelésre extra kedvezmény jár, összesen 100 dollár értékű termékekkel," + ", amikor 20% -os kedvezménypolitikát alkalmaz az extra kedvezményes megrendelésekre," + ", majd a kedvezmény utáni költség 80 dollár") @Test void teszt () kivételt dob {// megadott logikai érték jogosultForExtraDiscount = true; Megrendelés = new SpecialOrder (OrderFixtureUtils.orderLineItemsWorthNDollars (100), compatibleForExtraDiscount); SpecialDiscountPolicy discountPolicy = új SpecialDiscountPolicy () {@Orride public double discount (Order order) {return 0; } @Orride public double discount (SpecialOrder order) {if (order.isEligibleForExtraDiscount ()) return 0,20; visszatér 0,10; }}; // amikor Money totalCostAfterDiscount = order.totalCost (discountPolicy); // majd assertThat (totalCostAfterDiscount) .isEqualTo (Money.of (CurrencyUnit.USD, 80.00)); }
4. Következtetés