Programování

Přidejte do svých jarních aplikací jednoduchý modul pravidel

Jakýkoli netriviální softwarový projekt obsahuje netriviální množství takzvané obchodní logiky. Co přesně představuje obchodní logiku, je diskutabilní. V horách kódu vytvářeného pro typickou softwarovou aplikaci tu a tam kousky skutečně dělají práci, pro kterou byl software požadován - zpracovávat objednávky, ovládat zbraňové systémy, kreslit obrázky atd. Tyto bity ostře kontrastují s ostatními, které se zabývají vytrvalostí , protokolování, transakce, jazykové zvláštnosti, chyby rozhraní a další hlášky moderní podnikové aplikace.

Obchodní logika je častěji hluboce promíchána se všemi ostatními částmi. Když se používají těžké rušivé rámce (například Enterprise JavaBeans), je obzvláště obtížné zjistit, kde končí obchodní logika a kde začíná kód inspirovaný rámcem.

V dokumentech s definicí požadavků je zřídka uveden jeden softwarový požadavek, který však má sílu vytvořit nebo rozbít jakýkoli softwarový projekt: adaptabilita, měřítko snadné změny softwaru v reakci na změny v obchodním prostředí.

Moderní společnosti jsou nuceny být rychlé a flexibilní a chtějí to samé od svého podnikového softwaru. Obchodní pravidla, která byla dnes tak pečlivě implementována do obchodní logiky vašich tříd, zítra zastarají a bude třeba je rychle a přesně změnit. Když má váš kód obchodní logiku ukrytou hluboko v tunách a tunách těchto dalších bitů, úpravy se rychle stanou pomalými, bolestivými a náchylnými k chybám.

Není divu, že některými z nejtrendovějších oblastí podnikového softwaru jsou dnes motory pravidel a různé systémy pro správu podnikových procesů (BPM). Jakmile se podíváte na marketing-speak, tyto nástroje slibují v podstatě totéž: Svatý grál obchodní logiky zachycený v repozitáři, čistě odděleném a existujícím sám o sobě, připraveném k volání z jakékoli aplikace, kterou máte ve vaší softwarové společnosti.

Ačkoli komerční pravidla a systémy BPM mají mnoho výhod, obsahují také mnoho nedostatků. Snadnou volbou je cena, která může někdy snadno dosáhnout až sedmi číslic. Dalším nedostatkem je praktická standardizace, která dnes pokračuje i přes velké průmyslové úsilí a více dostupných papírových standardů. A protože stále více softwarových obchodů přizpůsobuje agilní, štíhlé a rychlé vývojové metodiky, je pro tyto těžké nástroje obtížné zapadnout.

V tomto článku vytváříme jednoduchý modul pravidel, který na jedné straně využívá jasné oddělení obchodní logiky typické pro takové systémy a na druhé straně - protože je založeno na populárním a výkonném rámci J2EE - není trpí složitostí a „neochvějností“ obchodních nabídek.

Jarní čas ve vesmíru J2EE

Poté, co se složitost podnikového softwaru stala nesnesitelnou a problém logiky podnikání vstoupil do centra pozornosti, se zrodil Spring Framework a další podobné systémy. Je pravděpodobné, že jaro je to nejlepší, co se podnikové Javě stalo za dlouhou dobu. Jaro poskytuje dlouhý seznam nástrojů a drobných vymožeností kódu, díky nimž je programování J2EE objektově orientovanější, mnohem jednodušší a zábavnější.

V srdci jara spočívá princip inverze kontroly. Jedná se o fantazijní a přetížené jméno, ale jde o tyto jednoduché nápady:

  • Funkčnost vašeho kódu je rozdělena na malé spravovatelné části
  • Tyto kousky jsou reprezentovány jednoduchými standardními fazolemi Java (jednoduché třídy Java, které vykazují část specifikace JavaBeans, ale ne všechny).
  • Děláš ne zapojte se do správy těchto fazolí (vytváření, ničení, nastavování závislostí)
  • Místo toho to na základě některých udělá kontejner Spring definice kontextu obvykle poskytovány ve formě souboru XML

Spring také poskytuje mnoho dalších funkcí, například kompletní a výkonný rámec Model-View-Controller pro webové aplikace, pohodlné obaly pro programování Java Database Connectivity a tucet dalších rámců. Ale tato témata přesahují rámec tohoto článku.

Než popíšu, co je potřeba k vytvoření jednoduchého pravidla pro aplikace založené na Spring, pojďme se zamyslet, proč je tento přístup dobrý nápad.

Návrhy motorů pravidel mají dvě zajímavé vlastnosti, díky nimž jsou užitečné:

  • Nejprve oddělují kód obchodní logiky od ostatních oblastí aplikace
  • Zadruhé, jsou externě konfigurovatelné, což znamená, že definice obchodních pravidel a to, jak a v jakém pořadí jsou spouštěny, jsou ukládány externě do aplikace a manipulovány tvůrcem pravidel, nikoli uživatelem aplikace nebo dokonce programátorem

Pružina se dobře hodí pro motor pravidel. Vysoce komponentní design správně kódované aplikace Spring podporuje umístění vašeho kódu do malého, spravovatelného, samostatný kousky (fazole), které lze externě konfigurovat pomocí definic kontextu Spring.

Čtěte dále a prozkoumejte tuto dobrou shodu mezi tím, co design pravidla vyžaduje, a tím, co již nabízí Spring design.

Návrh jarního pravidla

Náš design stavíme na interakci jarních fazolí Java, které nazýváme pravidla motoru komponenty. Pojďme definovat dva typy komponent, které můžeme potřebovat:

  • An akce je komponenta, která ve skutečnosti dělá něco užitečného v naší aplikační logice
  • A pravidlo je komponenta, která tvoří rozhodnutí v logickém toku akcí

Jelikož jsme velkými fanoušky dobrého objektově orientovaného designu, následující základní třída zachycuje základní funkčnost všech našich budoucích komponent, konkrétně schopnost být volána jinými komponentami s určitým argumentem:

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

Přirozeně je základní třída abstraktní, protože nikdy ji nebudeme potřebovat.

A teď kód pro AbstractAction, která bude rozšířena o další konkrétní konkrétní akce:

veřejná abstraktní třída AbstractAction rozšiřuje AbstractComponent {

soukromý AbstractComponent nextStep; public void execute (Object arg) vyvolá výjimku {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; }

}

Jak můžete vidět, AbstractAction dělá dvě věci: Ukládá definici další komponenty, kterou má vyvolat náš modul pravidel. A ve své vykonat() metoda, volá a doExecute () metoda bude definována konkrétní podtřídou. Po doExecute () vrátí, je vyvolána další komponenta, pokud existuje.

Náš AbstractRule je podobně jednoduchý:

veřejná abstraktní třída AbstractRule rozšiřuje AbstractComponent {

soukromý AbstractComponent positiveOutcomeStep; soukromý AbstractComponent negativeOutcomeStep; public void execute (Object arg) vyvolá Exception {boolean result = makeDecision (arg); if (výsledek) positiveOutcomeStep.execute (arg); else negativeOutcomeStep.execute (arg);

}

Protected abstract boolean makeDecision (Object arg) throws Exception;

// Getters and setters for positiveOutcomeStep and negativeOutcomeStep are omitted for brevity

Ve své vykonat() metoda, AbstractAction volá učinit rozhodnutí() metoda, kterou podtřída implementuje, a poté, v závislosti na výsledku této metody, zavolá jednu ze složek definovaných jako pozitivní nebo negativní výsledek.

Když to představíme, náš design je kompletní SpringRuleEngine třída:

veřejná třída SpringRuleEngine {private AbstractComponent firstStep; public void setFirstStep (AbstractComponent firstStep) {this.firstStep = firstStep; } public void processRequest (Object arg) vyvolá výjimku {firstStep.execute (arg); }}

To je vše, co je k hlavní třídě našeho pravidla engine: definice první komponenty v naší obchodní logice a způsob zahájení zpracování.

Ale počkejte, kde je instalace, která spojuje všechny naše třídy dohromady, aby mohly pracovat? Dále uvidíte, jak nám s tímto úkolem pomáhá kouzlo jara.

Pružinový motor pravidel v akci

Podívejme se na konkrétní příklad toho, jak by tento rámec mohl fungovat. Zvažte tento případ použití: musíme vyvinout aplikaci odpovědnou za zpracování žádostí o půjčku. Musíme splnit následující požadavky:

  • Zkontrolujeme úplnost aplikace a v opačném případě ji odmítneme
  • Zkontrolujeme, zda žádost přišla od žadatele žijícího ve státě, ve kterém máme oprávnění k podnikání
  • Zkontrolujeme, zda měsíční příjem a jeho měsíční výdaje zapadají do poměru, ve kterém se cítíme dobře
  • Příchozí aplikace jsou uloženy v databázi prostřednictvím persistenční služby, o které nic nevíme, kromě jejího rozhraní (možná její vývoj byl zadán do Indie)
  • Obchodní pravidla se mohou změnit, a proto je vyžadován návrh pravidla

Nejprve navrhneme třídu představující naši žádost o půjčku:

public class LoanApplication {public static final String INVALID_STATE = "Omlouváme se, ve vašem státě nepodnikáme"; public static final String INVALID_INCOME_EXPENSE_RATIO = "Je nám líto, ale vzhledem k poměru nákladů a výnosů nemůžeme půjčku poskytnout"; public static final String APPROVED = "Vaše žádost byla schválena"; public static final String INSUFFICIENT_DATA = "O své přihlášce jste neposkytli dostatek informací"; public static final String INPROGRESS = "probíhá"; public static final String [] STATUSES = new String [] {INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, APPROVED, INPROGRESS};

private String firstName; private String lastName; dvojitý soukromý příjem; soukromé dvojnásobné výdaje; private String stateCode; stav soukromého řetězce; public void setStatus (String status) {if (! Arrays.asList (STATUSES) .contains (status)) throw new IllegalArgumentException ("invalid status:" + status); this.status = status; }

// Parta dalších getterů a setterů je vynechána

}

Naše daná služba perzistence je popsána v následujícím rozhraní:

veřejné rozhraní LoanApplicationPersistenceInterface {public void recordApproval (aplikace LoanApplication) vyvolá výjimku; public void recordRejection (aplikace LoanApplication) vyvolá výjimku; public void recordIncomplete (aplikace LoanApplication) vyvolá výjimku; }

Toto rozhraní rychle zesměšňujeme vývojem a MockLoanApplicationPersistence třída, která nedělá nic jiného než uspokojuje smlouvu definovanou rozhraním.

Používáme následující podtřídu SpringRuleEngine třídy pro načtení kontextu Spring ze souboru XML a vlastně zahájení zpracování:

public class LoanProcessRuleEngine extends SpringRuleEngine {public static final SpringRuleEngine getEngine (String name) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("SpringRuleEngineContext.xml"); návrat (SpringRuleEngine) context.getBean (name); }}

V tuto chvíli máme kostru na místě, takže je ideální čas na napsání testu JUnit, který se objeví níže. Existuje několik předpokladů: Očekáváme, že naše společnost bude fungovat pouze ve dvou státech, v Texasu a Michiganu. A přijímáme pouze půjčky s poměrem nákladů a výnosů 70 procent nebo lepším.

veřejná třída SpringRuleEngineTest rozšiřuje TestCase {

public void testSuccessfulFlow () vyvolá výjimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication (); application.setFirstName ("John"); application.setLastName ("Doe"); application.setStateCode ("TX"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (aplikace); assertEquals (LoanApplication.APPROVED, application.getStatus ()); } public void testInvalidState () vyvolá výjimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication (); application.setFirstName ("John"); application.setLastName ("Doe"); application.setStateCode ("OK"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (aplikace); assertEquals (LoanApplication.INVALID_STATE, application.getStatus ()); } public void testInvalidRatio () vyvolá výjimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication (); application.setFirstName ("John"); application.setLastName ("Doe"); application.setStateCode ("MI"); application.setIncome (7000); application.setExpences (0,80 * 7000); // příliš vysoký engine.processRequest (aplikace); assertEquals (LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus ()); } public void testIncompleteApplication () vyvolá výjimku {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication (); engine.processRequest (aplikace); assertEquals (LoanApplication.INSUFFICIENT_DATA, application.getStatus ()); }

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