Programavimas

Pridėkite paprastą taisyklių variklį prie savo pavasario programų

Bet kuriame nereikšmingame programinės įrangos projekte yra nereikšmingas kiekis vadinamosios verslo logikos. Kas tiksliai sudaro verslo logiką, galima diskutuoti. Kalnuose, sukurtuose tipinei programinei įrangai, vienetai čia ir ten iš tikrųjų atlieka tą darbą, kurio prireikė programinei įrangai - apdoroja užsakymus, valdo ginklų sistemas, piešia paveikslėlius ir t. T. , registravimas, operacijos, kalbos keistenybės, pagrindo ypatybės ir kitos šiuolaikinės įmonės programos smulkmenos.

Dažniausiai verslo logika yra giliai maišoma su visais kitais dalykais. Kai naudojamos sunkios, įkyrios sistemos (pvz., „Enterprise JavaBeans“), ypač sunku suprasti, kur baigiasi verslo logika ir prasideda sistemos įkvėptas kodas.

Reikalavimų apibrėžimo dokumentuose yra retai išdėstytas vienas programinės įrangos reikalavimas, tačiau jis gali padaryti ar nutraukti bet kokį programinės įrangos projektą: pritaikomumas, matas, kaip lengva pakeisti programinę įrangą atsižvelgiant į verslo aplinkos pokyčius.

Šiuolaikinės įmonės yra priverstos būti greitos ir lanksčios, o to paties nori ir iš savo įmonės programinės įrangos. Verslo taisyklės, kurios šiandien buvo taip kruopščiai įgyvendintos jūsų klasių verslo logikoje, rytoj pasens ir jas reikės greitai ir tiksliai pakeisti. Kai jūsų verslo logika bus palaidota giliai tų kitų bitų tonose, modifikacijos greitai taps lėtos, skausmingos ir linkusios į klaidas.

Nenuostabu, kad kai kurios madingiausios įmonės programinės įrangos sritys šiandien yra taisyklių varikliai ir įvairios verslo procesų valdymo (BPM) sistemos. Peržvelgus rinkodaros kalbą, šie įrankiai žada iš esmės tą patį: saugykloje užfiksuotas, švariai atskirtas ir pats egzistuojantis „Verslo logikos gralis“, paruoštas iškviesti iš bet kurios jūsų programinės įrangos namuose esančios programos.

Nors komerciniai taisyklių varikliai ir BPM sistemos turi daug privalumų, jie taip pat turi daug trūkumų. Lengviausia pasirinkti kainą, kurią kartais gali lengvai pasiekti septyni skaitmenys. Kitas yra praktinio standartizavimo trūkumas, kuris tęsiasi ir šiandien, nepaisant didelių pramonės pastangų ir galimų popierinių standartų. Kai vis daugiau programinės įrangos parduotuvių pritaiko judrią, liesą ir greitą metodiką, tiems sunkiasvoriams įrankiams sunku pritapti.

Šiame straipsnyje mes sukursime paprastą taisyklių variklį, kuris, viena vertus, padės aiškiai atskirti tokioms sistemoms būdingą verslo logiką ir, kita vertus, nes jis yra paremtas populiaria ir galinga J2EE sistema - kenčia nuo komercinių pasiūlymų sudėtingumo ir „neatšaldymo“.

Pavasario laikas J2EE visatoje

Po to, kai įmonės programinės įrangos sudėtingumas tapo nepakeliamas ir verslo logikos problema atsidūrė dėmesio centre, gimė „Spring Framework“ ir kiti panašūs dalykai. Galima teigti, kad pavasaris yra geriausias dalykas, nutikęs įmonei „Java“ per ilgą laiką. Pavasaris pateikia ilgą sąrašą įrankių ir mažų kodų patogumų, dėl kurių „J2EE“ programavimas tampa labiau orientuotas į objektus, daug lengviau ir, dar labiau, įdomiau.

Pavasario širdyje slypi valdymo inversijos principas. Tai išgalvotas ir perkrautas vardas, tačiau tai susiję su šiomis paprastomis idėjomis:

  • Jūsų kodo funkcijos yra suskaidytos į mažas valdomas dalis
  • Šiuos gabalus vaizduoja paprastos standartinės „Java“ pupelės (paprastos „Java“ klasės, kuriose pateikiama dalis, bet ne visos „JavaBeans“ specifikacijos)
  • Tu darai ne įsitraukite į šių pupelių valdymą (priklausomybių kūrimas, naikinimas, nustatymas)
  • Vietoj to, „Spring“ konteineris tai daro už jus, remiantis kai kuriais konteksto apibrėžimas paprastai pateikiama XML failo forma

„Spring“ taip pat teikia daugybę kitų funkcijų, tokių kaip išsami ir galinga „Model-View-Controller“ sistema, skirta žiniatinklio programoms, „Java Database Connectivity“ programavimo patogumo paketai ir dar keliolika kitų sistemų. Tačiau šios temos nepatenka į šio straipsnio taikymo sritį.

Prieš aprašydamas, ko reikia norint sukurti paprastą taisyklių variklį, skirtą pavasarinėms programoms, pasvarstykime, kodėl šis požiūris yra gera idėja.

Taisyklių variklių dizainas turi dvi įdomias savybes, dėl kurių jie yra verti:

  • Pirma, jie atskiria verslo logikos kodą nuo kitų programos sričių
  • Antra, jie yra išoriškai konfigūruojamas, tai reiškia, kad verslo taisyklių apibrėžimai ir tai, kaip ir kokia tvarka jie suaktyvinami, saugomi išorėje programai ir jais manipuliuoja taisyklių kūrėjas, o ne programos vartotojas ar net programuotojas

Spyruoklė puikiai tinka įprastam varikliui. Labai sudėtingas tinkamai užkoduotos „Spring“ programos dizainas skatina jūsų kodą įdėti į mažus, valdomus, atskirai gabalėliai (pupelės), kuriuos išoriškai galima konfigūruoti pagal pavasario konteksto apibrėžimus.

Skaitykite toliau, norėdami išsiaiškinti šį gerą atitikimą tarp to, ko reikia taisyklės variklio dizainui, ir to, ką jau teikia pavasario dizainas.

„Spring“ tipo variklio dizainas

Savo dizainą grindžiame pavasarinių valdomų „Java“ pupelių, kurias mes vadiname, sąveika taisyklė variklio komponentai. Apibrėžkime dviejų tipų komponentus, kurių mums gali prireikti:

  • An veiksmas yra komponentas, kuris iš tikrųjų daro kažką naudingo mūsų programų logikoje
  • A taisyklė yra komponentas, kuris daro a sprendimas loginiame veiksmų sraute

Kadangi esame dideli gero objektyvaus dizaino gerbėjai, ši pagrindinė klasė atspindi visų būsimų mūsų komponentų pagrindines funkcijas, būtent galimybę kitus komponentus iškviesti su tam tikru argumentu:

public abstract class AbstractComponent {public abstract void execute (Object arg) meta Išimtis; }

Natūralu, kad pagrindinė klasė yra abstrakti, nes mums jos niekada nereikės.

Ir dabar koduokite SantraukaVeikimas, kuris bus pratęstas kitais būsimais konkrečiais veiksmais:

public abstract class AbstractAction prailgina AbstractComponent {

privatus „AbstractComponent nextStep“; public void execute (Object arg) išmeta išimtį {this.doExecute (arg); if (nextStep! = null) nextStep.execute (arg); } apsaugotas abstraktus void doExecute (Object arg) išmeta Exception;

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

public AbstractComponent getNextStep () {return nextStep; }

}

Kaip matai, SantraukaVeikimas daro du dalykus: jame saugomas kito komponento, į kurį turi kreiptis mūsų taisyklių variklis, apibrėžimas. Ir, jos vykdyti () metodas, jis vadina a „doExecute“ () metodas turi būti apibrėžtas konkrečiu poklasiu. Po „doExecute“ () grįžta, iškviečiamas kitas komponentas, jei yra.

Mūsų AbstractRule yra panašiai paprastas:

public abstract class AbstractRule pratęsia AbstractComponent {

privatus SantraukaKomponentas teigiamas RezultatasŽingsnis; privatus SantraukaKomponentas neigiamasPagalbos žingsnis; public void execute (Object arg) meta išimtį {loginis rezultatas = makeDecision (arg); if (rezultatas) teigiamas RezultatasStep.execute (arg); dar neigiamasOutcomeStep.execute (arg);

}

apsaugotas abstraktus loginis „makeDecision“ („Object arg“) meta išimtį;

// Gauters ir derintojai teigiamam rezultatui ir neigiamam rezultatui yra praleidžiami dėl trumpumo

Savo vykdyti () metodas SantraukaVeikimas skambina priimti sprendimą() metodas, kurį įgyvendina poklasis, o paskui, priklausomai nuo to metodo rezultato, vieną iš komponentų vadina teigiamais arba neigiamais rezultatais.

Mūsų dizainas yra baigtas, kai tai pristatome „SpringRuleEngine“ klasė:

viešoji klasė „SpringRuleEngine“ {private AbstractComponent firstStep; public void setFirstStep (AbstractComponent firstStep) {this.firstStep = firstStep; } public void processRequest (Object arg) išmeta išimtį {firstStep.execute (arg); }}

Tai viskas, kas yra pagrindinėje mūsų taisyklių variklio klasėje: pirmojo verslo logikos komponento apibrėžimas ir metodas pradėti apdoroti.

Bet palaukite, kur yra santechnika, jungianti visas mūsų klases, kad jie galėtų dirbti? Toliau pamatysite, kaip pavasario magija padeda mums atlikti šią užduotį.

Pavasarinis taisyklių variklis veikia

Pažvelkime į konkretų pavyzdį, kaip ši sistema gali veikti. Apsvarstykite šį naudojimo atvejį: turime sukurti programą, atsakingą už paskolų paraiškų apdorojimą. Turime atitikti šiuos reikalavimus:

  • Mes patikriname paraiškos išsamumą ir atmetame ją kitaip
  • Mes tikriname, ar prašymą pateikė pareiškėjas, gyvenantis valstybėje, kurioje esame įgalioti verstis verslu
  • Mes patikriname, ar pareiškėjo mėnesinės pajamos ir jo (jos) mėnesio išlaidos sutampa su santykiu, su kuriuo jaučiamės patogiai
  • Gaunamos programos saugomos duomenų bazėje per atkaklumo paslaugą, apie kurią nieko nežinome, išskyrus jos sąsają (galbūt jos kūrimas buvo perduotas Indijai)
  • Verslo taisyklės gali keistis, todėl reikalingas taisyklių variklio dizainas

Pirmiausia suprojektuokime klasę, atspindinčią mūsų paskolos paraišką:

public class LoanApplication {public static final String INVALID_STATE = "Atsiprašome, bet jūsų valstybėje nevykdome verslo"; public static final String INVALID_INCOME_EXPENSE_RATIO = "Deja, negalime suteikti paskolos, atsižvelgiant į šį išlaidų / pajamų santykį"; public static final String APPROVED = "Jūsų paraiška patvirtinta"; public static final String INSUFFICIENT_DATA = "Nepateikėte pakankamai informacijos apie savo programą"; public static final String INPROGRESS = "vykdoma"; viešoji statinė galutinė eilutė [] STATUSES = nauja eilutė [] {INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, PATVIRTINTA, INPROGRESS};

privati ​​eilutė firstName; privati ​​eilutės pavardė; privačios dvigubos pajamos; privačios dvigubos išlaidos; privatus eilutės būsenos kodas; privačios stygos statusas; public void setStatus (String status) {if (! Arrays.asList (STATUSES) .contains (status)) thrown new IllegalArgumentException ("neteisinga būsena:" + būsena); this.status = status; }

Daugybė kitų getters ir seters yra praleisti

}

Mūsų teikiamą patvarumo paslaugą apibūdina ši sąsaja:

viešoji sąsaja „LoanApplicationPersistenceInterface“ {public void recordApproval („LoanApplication“ programa) išmeta Išimtį; public void recordReject („LoanApplication“ programa) išmeta išimtį; public void recordIncomplete („LoanApplication“ programa) išmeta išimtį; }

Mes greitai pasišaipome iš šios sąsajos, sukurdami a „MockLoanApplicationPersistence“ klasė, kuri nieko nedaro, bet tenkina sąsajoje apibrėžtą sutartį.

Mes naudojame šį poklasį „SpringRuleEngine“ klasę įkelti „Spring“ kontekstą iš XML failo ir iš tikrųjų pradėti apdoroti:

public class LoanProcessRuleEngine pratęsia SpringRuleEngine {public static final SpringRuleEngine getEngine (String name) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("SpringRuleEngineContext.xml"); return (SpringRuleEngine) context.getBean (vardas); }}

Šiuo metu mes turime savo griaučius, todėl pats tinkamiausias laikas parašyti JUnit testą, kuris pasirodo žemiau. Pateikiamos kelios prielaidos: mes tikimės, kad mūsų įmonė veiks tik dviejose valstijose - Teksase ir Mičigane. Mes priimame tik tas paskolas, kurių išlaidų / pajamų santykis yra 70 proc. Ar didesnis.

viešoji klasė „SpringRuleEngineTest“ pratęsia „TestCase“ {

public void testSuccessfulFlow () meta išimtį {SpringRuleEngine variklis = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); „LoanApplication“ paraiška = nauja „LoanApplication“); application.setFirstName („Jonas“); application.setLastName („Doe“); application.setStateCode („TX“); application.setExpences (4500); application.setIncome (7000); engine.processRequest (paraiška); assertEquals (LoanApplication.APPROVED, application.getStatus ()); } public void testInvalidState () meta išimtį {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); „LoanApplication“ paraiška = nauja „LoanApplication“); application.setFirstName („Jonas“); application.setLastName („Doe“); application.setStateCode („Gerai“); application.setExpences (4500); application.setIncome (7000); engine.processRequest (paraiška); assertEquals (LoanApplication.INVALID_STATE, application.getStatus ()); } public void testInvalidRatio () meta išimtį {SpringRuleEngine variklis = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); „LoanApplication“ paraiška = nauja „LoanApplication“); application.setFirstName („Jonas“); application.setLastName („Doe“); application.setStateCode („MI“); application.setIncome (7000); application.setExpences (0,80 * 7000); // per didelis variklis.processRequest (taikymas); assertEquals (LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus ()); } public void testIncompleteApplication () meta išimtį {SpringRuleEngine variklis = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); „LoanApplication“ paraiška = nauja „LoanApplication“); engine.processRequest (paraiška); assertEquals (LoanApplication.INSUFFICIENT_DATA, application.getStatus ()); }

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