Programovanie

Pridajte do svojich jarných aplikácií jednoduchý modul pravidiel

Akýkoľvek netriviálny softvérový projekt obsahuje netriviálne množstvo takzvanej obchodnej logiky. Čo presne predstavuje obchodnú logiku, je diskutabilné. V horách kódu vytvoreného pre typickú softvérovú aplikáciu tu a tam kúsky skutočne fungujú tak, ako to bolo od softvéru vyžadované - spracovanie objednávok, riadenie zbraňových systémov, kreslenie obrázkov atď. Tieto kúsky ostro kontrastujú s ostatnými, ktoré sa zaoberajú vytrvalosťou , protokolovanie, transakcie, jazykové zvláštnosti, chyby v architektúre a ďalšie oznamy o modernej podnikovej aplikácii.

Oveľa častejšie je obchodná logika hlboko zmiešaná so všetkými ostatnými kúskami. Keď sa používajú ťažké a rušivé rámce (napríklad Enterprise JavaBeans), je obzvlášť ťažké rozlíšiť, kde končí obchodná logika a kde sa začne kód inšpirovaný rámcom.

V dokumentoch definícií požiadaviek je zriedka uvedená jedna požiadavka na softvér, ktorá má však právomoc vytvoriť alebo zlomiť akýkoľvek softvérový projekt: adaptabilita, miera ľahkosti zmeny softvéru v reakcii na zmeny v obchodnom prostredí.

Moderné spoločnosti sú nútené byť rýchle a flexibilné a to isté chcú aj od svojho podnikového softvéru. Obchodné pravidlá, ktoré sa dnes tak dôsledne implementovali do obchodnej logiky vašich tried, budú zajtra zastarané a bude potrebné ich rýchlo a presne zmeniť. Keď má váš kód obchodnú logiku zakomponovanú hlboko do ton a ton ďalších bitov, úpravy sa rýchlo stanú pomalými, bolestivými a náchylnými na chyby.

Niet divu, že medzi najtrendovejšie oblasti podnikového softvéru v súčasnosti patria motory pravidiel a rôzne systémy riadenia obchodných procesov (BPM). Len čo sa pozriete do marketingu, tieto nástroje sľubujú v podstate to isté: Svätý grál obchodnej logiky zachytený v archíve, ktorý je čisto oddelený a existuje sám osebe a je možné ho zavolať z akejkoľvek aplikácie, ktorú máte vo svojom softvérovom dome.

Aj keď systémy na vytváranie komerčných pravidiel a systémy BPM majú veľa výhod, obsahujú aj veľa nedostatkov. Najjednoduchšou z nich je cena, ktorá niekedy môže ľahko dosiahnuť sedem číslic. Ďalším problémom je chýbajúca praktická štandardizácia, ktorá dnes pretrváva napriek veľkému úsiliu odvetvia a mnohým dostupným papierovým štandardom. A keďže čoraz viac softvérových obchodov prispôsobuje agilné, štíhle a rýchle vývojové metodiky, je pre tieto ťažké nástroje ťažké zapadnúť.

V tomto článku zostavujeme jednoduchý mechanizmus pravidiel, ktorý na jednej strane využíva jasné oddelenie obchodnej logiky typickej pre takéto systémy a na druhej strane - pretože je chránený populárnym a výkonným rámcom J2EE - nie trpia zložitosťou a „nechutnosťou“ obchodných ponúk.

Jarný čas vo vesmíre J2EE

Po tom, čo sa zložitosť podnikového softvéru stala neúnosnou a do centra pozornosti sa dostal problém obchodnej logiky, sa zrodil Spring Framework a ďalšie podobné riešenia. Je pravdepodobné, že jar je najlepšia vec, ktorá sa stala podnikovej Jave za dlhý čas. Jar poskytuje dlhý zoznam nástrojov a drobných vymožeností kódu, vďaka ktorým je programovanie J2EE objektovejšie, oveľa jednoduchšie a zábavnejšie.

V srdci jari spočíva princíp inverzie kontroly. Toto je vymyslený a preťažený názov, ale prichádza k týmto jednoduchým nápadom:

  • Funkčnosť vášho kódu je rozdelená na malé spravovateľné kúsky
  • Tieto kúsky sú reprezentované jednoduchými štandardnými fazuľami Java (jednoduché triedy Java, ktoré vykazujú niektoré, ale nie všetky, špecifikácie JavaBeans).
  • Ty robíš nie zapojte sa do riadenia týchto fazúľ (vytváranie, ničenie, nastavovanie závislostí)
  • Namiesto toho to na základe niektorých urobí kontajner Spring definícia kontextu zvyčajne poskytované vo forme súboru XML

Jar tiež poskytuje mnoho ďalších funkcií, ako napríklad kompletný a výkonný rámec Model-View-Controller pre webové aplikácie, pohodlné obaly pre programovanie Java Database Connectivity a tucet ďalších rámcov. Tieto témy však presahujú rámec tohto článku.

Predtým, ako opíšem, čo je potrebné na vytvorenie jednoduchého pravidla pre aplikácie založené na jar, zvážme, prečo je tento prístup dobrý nápad.

Dizajn motorov s pravidlami má dve zaujímavé vlastnosti, vďaka ktorým sa im oplatí:

  • Najprv oddelia kód obchodnej logiky od ostatných oblastí aplikácie
  • Po druhé, sú externe konfigurovateľné, To znamená, že definície obchodných pravidiel a to, ako a v akom poradí sa spúšťajú, sú uložené externe k aplikácii a manipulované tvorcom pravidla, nie používateľom aplikácie alebo dokonca programátorom.

Pružina sa dobre hodí pre motor s predpismi. Vysoko komponentný dizajn správne kódovanej aplikácie Spring podporuje umiestnenie vášho kódu do malých, zvládnuteľných, oddelene kúsky (fazuľa), ktoré je možné externe konfigurovať pomocou jarných definícií kontextu.

Čítajte ďalej a preskúmajte túto dobrú zhodu medzi tým, čo návrh pravidla vyžaduje, a tým, čo už poskytuje jarný dizajn.

Dizajn pružinového pravidla

Pri návrhu vychádzame z interakcie jarných fazúľ Java, ktoré nazývame pravidlo komponentov motora. Poďme definovať dva typy komponentov, ktoré by sme mohli potrebovať:

  • An akcia je komponent, ktorý v našej aplikačnej logike skutočne robí niečo užitočné
  • A pravidlo je komponent, ktorý vytvára rozhodnutie v logickom toku akcií

Pretože sme veľkými fanúšikmi dobrého objektovo orientovaného dizajnu, nasledujúca základná trieda zachytáva základnú funkčnosť všetkých našich budúcich komponentov, konkrétne schopnosť byť volaná inými komponentmi s určitými argumentmi:

public abstract class AbstractComponent {public abstract void execute (Object arg) throws Exception; }

Prirodzene, základná trieda je abstraktná, pretože nikdy ju nebudeme potrebovať.

A teraz kód pre AbstractAction, ktoré sa rozšíria o ďalšie konkrétne opatrenia v budúcnosti:

verejná abstraktná trieda AbstractAction rozširuje AbstractComponent {

súkromná AbstractComponent nextStep; public void execute (Object arg) vyvolá výnimku {this.doExecute (arg); if (nextStep! = null) nextStep.execute (arg); } protected abstract void doExecute (Object arg) throws Exception;

public void setNextStep (AbstractComponent nextStep) {this.nextStep = nextStep; }

public AbstractComponent getNextStep () {návrat nextStep; }

}

Ako môžeš vidieť, AbstractAction robí dve veci: Ukladá definíciu nasledujúcej súčasti, ktorú má vyvolať náš nástroj na vytváranie pravidiel. A vo svojom vykonať () metóda, volá sa a doExecute () metóda bude definovaná konkrétnou podtriedou. Po doExecute () vráti, vyvolá sa ďalší komponent, ak existuje.

Náš AbstractRule je podobne jednoduchý:

verejná abstraktná trieda AbstractRule rozširuje AbstractComponent {

súkromná AbstractComponent positiveOutcomeStep; súkromná AbstractComponent negativeOutcomeStep; public void execute (Object arg) hodí Exception {boolean result = makeDecision (arg); if (výsledok) positiveOutcomeStep.execute (arg); else negativeOutcomeStep.execute (arg);

}

chránený abstraktný booleovský makeDecision (Object arg) vyvolá výnimku;

// Kvôli stručnosti sú vynechaní hľadači a nastavovatelia pre positiveOutcomeStep a negativeOutcomeStep

V jeho vykonať () metóda, AbstractAction žiada urobiť rozhodnutie() metóda, ktorú podtrieda implementuje, a potom v závislosti od výsledku tejto metódy zavolá jednu zo zložiek definovaných ako pozitívny alebo negatívny výsledok.

Náš návrh je dokončený, keď to predstavíme SpringRuleEngine trieda:

verejná trieda SpringRuleEngine {private AbstractComponent firstStep; public void setFirstStep (AbstractComponent firstStep) {this.firstStep = firstStep; } public void processRequest (Object arg) hodí výnimku {firstStep.execute (arg); }}

To je všetko, čo je v hlavnej triede nášho pravidla engine: definícia prvého komponentu v našej obchodnej logike a spôsob začatia spracovania.

Ale počkajte, kde je vodovod, ktorý spája všetky naše triedy, aby mohli pracovať? Ďalej uvidíte, ako nám s touto úlohou pomáha kúzlo jari.

Pružinový motor pravidiel v akcii

Pozrime sa na konkrétny príklad toho, ako by tento rámec mohol fungovať. Zvážte tento prípad použitia: musíme vyvinúť aplikáciu zodpovednú za spracovanie žiadostí o pôžičku. Musíme splniť nasledujúce požiadavky:

  • Skontrolujeme úplnosť žiadosti a inak ju zamietneme
  • Skontrolujeme, či žiadosť prišla od uchádzača žijúceho v štáte, v ktorom máme oprávnenie na podnikanie
  • Skontrolujeme, či mesačný príjem a jeho mesačné výdavky zapadajú do pomeru, v ktorom sa cítime dobre
  • Prichádzajúce aplikácie sú uložené v databáze prostredníctvom persistenčnej služby, o ktorej nič nevieme, okrem jej rozhrania (možno bol jeho vývoj outsourcovaný do Indie)
  • Obchodné pravidlá sa môžu meniť, a preto je potrebný návrh pravidla

Najskôr navrhnime triedu predstavujúcu našu žiadosť o pôžičku:

public class LoanApplication {public static final String INVALID_STATE = "Je nám ľúto, že vo vašom štáte nepodnikáme"; public static final String INVALID_INCOME_EXPENSE_RATIO = "Je nám ľúto, ale vzhľadom na pomer výdavkov a výnosov nemôžeme poskytnúť pôžičku"; public static final String APPROVED = "Vaša žiadosť bola schválená"; public static final String INSUFFICIENT_DATA = "O svojej žiadosti ste neposkytli dostatok informácií"; public static final String INPROGRESS = "prebieha"; verejný statický konečný reťazec [] STATUSES = nový reťazec [] {INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, SCHVÁLENÉ, INPROGRESS};

private String meno; private String priezvisko; dvojitý súkromný príjem; súkromné ​​dvojité výdavky; private String stateCode; stav súkromného reťazca; public void setStatus (stav reťazca) {if (! Arrays.asList (STATUSES) .contains (status)) hodiť novú IllegalArgumentException ("neplatný stav:" + stav); this.status = stav; }

// Parta ďalších getrov a setterov je vynechaná

}

Našu danú službu vytrvalosti popisuje nasledujúce rozhranie:

verejné rozhranie LoanApplicationPersistenceInterface {public void recordApproval (aplikácia LoanApplication) vyvolá výnimku; public void recordRejection (aplikácia LoanApplication) vyvolá výnimku; public void recordIncomplete (aplikácia LoanApplication) vyvolá výnimku; }

Toto rozhranie sa rýchlo vysmievame vývojom a MockLoanApplicationPersistence trieda, ktorá nerobí nič iné ako uspokojuje zmluvu definovanú rozhraním.

Používame nasledujúcu podtriedu SpringRuleEngine triedy, aby sa načítal jarný kontext zo súboru XML a skutočne začalo spracovanie:

public class LoanProcessRuleEngine extends SpringRuleEngine {public static final SpringRuleEngine getEngine (názov reťazca) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("SpringRuleEngineContext.xml"); návrat (SpringRuleEngine) context.getBean (meno); }}

V tejto chvíli máme kostru na svojom mieste, takže je ideálny čas na napísanie testu JUnit, ktorý sa objaví nižšie. Existuje niekoľko predpokladov: Očakávame, že naša spoločnosť bude pôsobiť iba v dvoch štátoch, v Texase a Michigane. A prijímame iba pôžičky s pomerom výdavkov a výnosov 70 percent alebo lepším.

verejná trieda SpringRuleEngineTest rozširuje TestCase {

public void testSuccessfulFlow () hodí výnimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); Aplikácia LoanApplication = nová LoanApplication (); application.setFirstName ("John"); application.setLastName ("Srnka"); application.setStateCode ("TX"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (aplikácia); assertEquals (LoanApplication.APPROVED, application.getStatus ()); } public void testInvalidState () hodí výnimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); Aplikácia LoanApplication = nová LoanApplication (); application.setFirstName ("John"); application.setLastName ("Srnka"); application.setStateCode ("OK"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (aplikácia); assertEquals (LoanApplication.INVALID_STATE, application.getStatus ()); } public void testInvalidRatio () vyvolá výnimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); Aplikácia LoanApplication = nová LoanApplication (); application.setFirstName ("John"); application.setLastName ("Srnka"); application.setStateCode ("MI"); application.setIncome (7000); application.setExpences (0,80 * 7000); // príliš vysoký engine.processRequest (application); assertEquals (LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus ()); } public void testIncompleteApplication () vyvolá výnimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); Aplikácia LoanApplication = nová LoanApplication (); engine.processRequest (aplikácia); assertEquals (LoanApplication.INSUFFICIENT_DATA, application.getStatus ()); }

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