Programmēšana

Pievienojiet savām pavasara lietojumprogrammām vienkāršu noteikumu dzinēju

Jebkurā nedzīvajā programmatūras projektā ir nenozīmīgs daudzums tā sauktās biznesa loģikas. Par to, kas tieši veido biznesa loģiku, var strīdēties. Kodu kalnos, kas ražoti tipiskai programmatūras lietojumprogrammai, gabali šeit un tur faktiski veic programmatūras pieprasīto darbu - apstrādā pasūtījumus, kontrolē ieroču sistēmas, zīmē attēlus utt. Šie biti krasi kontrastē ar citiem, kas nodarbojas ar neatlaidību. , reģistrēšana, darījumi, valodas dīvainības, ietvaru dīvainības un citi mūsdienu uzņēmuma lietojumprogrammas sīkumi.

Biežāk nekā nav, biznesa loģika ir dziļi sajaukta ar visiem šiem citiem gabaliem. Ja tiek izmantotas smagas, uzmācīgas sistēmas (piemēram, Enterprise JavaBeans), īpaši grūti noteikt, kur beidzas biznesa loģika un sākas ietvaru iedvesmots kods.

Ir viena programmatūras prasība, kas prasību definēšanas dokumentos ir reti aprakstīta, tomēr tai ir tiesības izveidot vai pārtraukt jebkuru programmatūras projektu: pielāgojamība, pasākums, cik viegli ir mainīt programmatūru, reaģējot uz uzņēmējdarbības vides izmaiņām.

Mūsdienu uzņēmumi ir spiesti būt ātri un elastīgi, un to pašu vēlas arī no sava uzņēmuma programmatūras. Biznesa noteikumi, kas tik cītīgi tika ieviesti jūsu klases biznesa loģikā šodien, rīt būs novecojuši, un tie būs jāmaina ātri un precīzi. Kad kodā biznesa loģika ir aprakta daudzu citu bitu tonīs, modifikācija ātri kļūs lēna, sāpīga un pakļauta kļūdām.

Nav brīnums, ka dažas no modernākajām uzņēmuma programmatūras jomām mūsdienās ir noteikumu dzinēji un dažādas biznesa procesu vadības (BPM) sistēmas. Kad esat iepazinies ar mārketinga runu, šie rīki būtībā sola to pašu: Biznesa loģikas Svētais Grāls, kas uzņemts krātuvē, tīri nodalīts un pats par sevi pastāv, gatavs izsaukšanai no jebkuras lietojumprogrammas, kas jums var būt jūsu programmatūras namā.

Lai gan komerciāliem noteikumu motoriem un BPM sistēmām ir daudz priekšrocību, tajos ir arī daudz trūkumu. Viegli izvēlēties cenu, kas dažreiz var viegli sasniegt septiņus ciparus. Cits ir praktiskas standartizācijas trūkums, kas turpinās arī šodien, neskatoties uz lielajiem nozares centieniem un vairākiem pieejamajiem papīra standartiem. Un, tā kā arvien vairāk programmatūras veikalu pielāgo veiklas, liesas un ātras izstrādes metodikas, šiem smagajiem rīkiem ir grūti iekļauties.

Šajā rakstā mēs izveidojam vienkāršu noteikumu dzinēju, kas, no vienas puses, nodrošina skaidru uzņēmējdarbības loģikas nošķiršanu, kas raksturīga šādām sistēmām, un, no otras puses, jo tā ir cūciņa balstīta uz populāro un spēcīgo J2EE sistēmu - cieš no komerciālo piedāvājumu sarežģītības un "bezjēdzības".

Pavasara laiks J2EE Visumā

Pēc tam, kad uzņēmuma programmatūras sarežģītība kļuva nepanesama un biznesa loģikas problēma nonāca uzmanības centrā, dzima Spring Framework un citi līdzīgi. Var teikt, ka pavasaris ir labākais, kas ilgu laiku notika ar Java uzņēmumu. Pavasaris nodrošina garu rīku un nelielu kodu ērtību sarakstu, kas padara J2EE programmēšanu objektorientētāku, daudz vienkāršāku un, labi, jautrāku.

Pavasara sirdī slēpjas vadības inversijas princips. Šis ir izdomāts un pārslogots nosaukums, taču tas attiecas uz šīm vienkāršajām idejām:

  • Jūsu koda funkcionalitāte ir sadalīta mazos pārvaldāmos gabalos
  • Šos gabalus attēlo vienkāršas, standarta Java pupiņas (vienkāršas Java klases, kas parāda dažas, bet ne visas JavaBeans specifikācijas)
  • Jūs darāt iesaistīties šo pupiņu pārvaldībā (atkarību radīšana, iznīcināšana, iestatīšana)
  • Tā vietā Spring konteiners to dara jūsu vietā, pamatojoties uz dažiem konteksta definīcija parasti sniedz XML faila formā

Pavasaris nodrošina arī daudzas citas funkcijas, piemēram, pilnīgu un jaudīgu Model-View-Controller ietvaru tīmekļa lietojumprogrammām, ērtības ietinējus Java Database Connectivity programmēšanai un duci citu ietvaru. Bet šie priekšmeti ir krietni ārpus šī raksta darbības jomas.

Pirms es aprakstīšu, kas nepieciešams, lai izveidotu vienkāršu noteikumu dzinēju pavasarī balstītām lietojumprogrammām, apsvērsim, kāpēc šī pieeja ir laba ideja.

Noteikumu motora dizainam ir divas interesantas īpašības, kas padara tos vērtīgus:

  • Pirmkārt, tie nošķir biznesa loģikas kodu no citām lietojumprogrammas jomām
  • Otrkārt, viņi ir ārēji konfigurējams, kas nozīmē, ka uzņēmējdarbības kārtulas definīcijas un to, kā un kādā secībā tās tiek aktivizētas, tiek glabātas ārpus lietojumprogrammas un ar tām manipulē noteikumu veidotājs, nevis lietojumprogrammas lietotājs vai pat programmētājs

Pavasaris nodrošina labu piemērotību kārtējo motoru. Pareizi kodētas Spring lietojumprogrammas ļoti komponētais dizains veicina koda ievietošanu mazos, pārvaldāmos, atsevišķi gabali (pupiņas), kurus var ārēji konfigurēt, izmantojot pavasara konteksta definīcijas.

Lasiet tālāk, lai izpētītu šo labo saskaņu starp to, kas vajadzīgs noteikumu dzinēja dizainam un ko pavasara dizains jau nodrošina.

Pavasara bāzes motora dizains

Mēs balstāmies uz pavasara kontrolēto Java pupiņu mijiedarbību, ko mēs saucam likt motora komponentus. Definēsim divu veidu komponentus, kas mums varētu būt nepieciešami:

  • An darbība ir komponents, kas faktiski padara kaut ko noderīgu mūsu lietojumprogrammu loģikā
  • A likums ir komponents, kas padara a lēmumu loģiskā darbību plūsmā

Tā kā mēs esam lieli laba objektorientēta dizaina cienītāji, šāda bāzes klase atspoguļo visu mūsu nākamo komponentu bāzes funkcionalitāti, proti, iespēju citiem komponentiem izsaukt ar kādu argumentu:

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

Protams, bāzes klase ir abstrakta, jo mums tā pati nekad nebūs vajadzīga.

Un tagad kods AbstractAction, kas jāpaplašina ar citām turpmākajām konkrētām darbībām:

publiskā abstraktā klase AbstractAction paplašina AbstractComponent {

privāts AbstractComponent nextStep; public void execute (Object arg) izmet izņēmumu {this.doExecute (arg); if (nextStep! = null) nextStep.execute (arg); } aizsargāts abstrakts void doExecute (Object arg) izmet Izņēmums;

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

public AbstractComponent getNextStep () {return nextStep; }

}

Kā jūs redzat, AbstractAction veic divas lietas: tajā tiek glabāta nākamā komponenta definīcija, uz kuru jāizmanto mūsu noteikumu dzinējs. Un, tā izpildīt() metodi, tā izsauc a doExecute () metodi, kas jānosaka ar konkrētu apakšklasi. Pēc doExecute () atgriežas, tiek izsaukts nākamais komponents, ja tāds ir.

Mūsu AbstractRule ir līdzīgi vienkāršs:

publiskā abstraktā klase AbstractRule paplašina AbstractComponent {

privāts AbstractComponent pozitívOutcomeStep; privāts AbstractComponent negativeOutcomeStep; public void execute (Object arg) izmet izņēmumu {boolean result = makeDecision (arg); if (iznākums) pozitívOutcomeStep.execute (arg); cits negatīvsOutcomeStep.execute (arg);

}

aizsargāts abstrakts būla mērogsLēmums (Object arg) izmet Izņēmums;

// Īsuma labad tiek izlaisti pozitīvā un rezultāta soļa un negatīvā rezultāta soli un iestatītāji

izpildīt() metodi AbstractAction aicina makeDecision () metodi, kuru apakšklase ievieš, un pēc tam atkarībā no šīs metodes rezultāta vienu no komponentiem sauc par pozitīvu vai negatīvu rezultātu.

Mūsu dizains ir pabeigts, kad mēs to ieviešam SpringRuleEngine klase:

publiskā klase SpringRuleEngine {private AbstractComponent firstStep; public void setFirstStep (AbstractComponent firstStep) {this.firstStep = firstStep; } public void processRequest (Object arg) izmet izņēmumu {firstStep.execute (arg); }}

Tas ir viss, kas ir mūsu noteikumu dzinēja galvenajā klasē: mūsu biznesa loģikas pirmā komponenta definīcija un apstrādes sākšanas metode.

Bet pagaidiet, kur ir santehnika, kas visas mūsu klases vada kopā, lai viņi varētu strādāt? Pēc tam jūs redzēsiet, kā pavasara burvība mums palīdz šajā uzdevumā.

Pavasarī balstīts noteikumu motors darbībā

Apskatīsim konkrētu piemēru tam, kā šī sistēma varētu darboties. Apsveriet šo izmantošanas gadījumu: mums jāizstrādā lietojumprogramma, kas atbild par aizdevuma pieteikumu apstrādi. Mums jāatbilst šādām prasībām:

  • Mēs pārbaudām pieteikuma pilnīgumu un noraidām citādi
  • Mēs pārbaudām, vai pieteikumu iesniedza pieteikuma iesniedzējs, kurš dzīvo valstī, kurā mums ir atļauts veikt uzņēmējdarbību
  • Mēs pārbaudām, vai pretendenta ikmēneša ienākumi un viņa / viņas ikmēneša izdevumi iekļaujas proporcijā, kurā mēs jūtamies ērti
  • Ienākošās lietojumprogrammas tiek glabātas datu bāzē, izmantojot pastāvības pakalpojumu, par kuru mēs neko nezinām, izņemot tā saskarni (iespējams, tā izstrāde tika uzticēta Indijai)
  • Uzņēmējdarbības noteikumi var mainīties, tāpēc ir nepieciešams noteikumu motora dizains

Vispirms izveidosim klasi, kas atspoguļo mūsu aizdevuma pieteikumu:

public class LoanApplication {public static final String INVALID_STATE = "Atvainojiet, ka mēs neveicam uzņēmējdarbību jūsu valstī"; public static final String INVALID_INCOME_EXPENSE_RATIO = "Atvainojiet, ka mēs nevaram piešķirt aizdevumu, ņemot vērā šo izdevumu / ienākumu attiecību"; public static final String APPROVED = "Jūsu pieteikums ir apstiprināts"; public static final String INSUFFICIENT_DATA = "Jūs nesniedzāt pietiekami daudz informācijas par savu lietojumprogrammu"; public static final String INPROGRESS = "notiek"; publiskā statiskā galīgā virkne [] STATUSES = jauna virkne [] {INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, APSTIPRINĀTS, INPROGRESS};

privāta virkne firstName; privāta virknes uzvārds; privāti dubulti ienākumi; privātas dubultās izmaksas; privāta virknes stāvokļa kods; privātas stīgas statuss; public void setStatus (String status) {if (! Arrays.asList (STATUSES) .contains (status)) mest jaunu IllegalArgumentException ("nederīgs statuss:" + statuss); this.status = statuss; }

// Citu getters un seters ir izlaisti

}

Mūsu doto noturības pakalpojumu apraksta šāda saskarne:

publiskā saskarne LoanApplicationPersistenceInterface {public void recordApproval (LoanApplication lietojumprogramma) izmet izņēmumu; public void recordRejection (LoanApplication pieteikums) met izņēmumu; public void recordIncomplete (LoanApplication pieteikums) izmet Izņēmums; }

Mēs ātri ņirgājamies par šo saskarni, izstrādājot MockLoanApplicationPersistence klase, kas neko nedara, bet izpilda saskarnes noteikto līgumu.

Mēs izmantojam šādu apakšklasi SpringRuleEngine klases, lai ielādētu pavasara kontekstu no XML faila un faktiski sāktu apstrādi:

public class LoanProcessRuleEngine paplašina SpringRuleEngine {public static final SpringRuleEngine getEngine (virknes nosaukums) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("SpringRuleEngineContext.xml"); return (SpringRuleEngine) context.getBean (nosaukums); }}

Šajā brīdī mums ir skelets, tāpēc ir īstais laiks uzrakstīt JUnit testu, kas parādās zemāk. Tiek izteikti daži pieņēmumi: Mēs sagaidām, ka mūsu uzņēmums darbosies tikai divos štatos - Teksasā un Mičiganā. Un mēs pieņemam tikai aizdevumus, kuru izdevumu / ienākumu attiecība ir 70 procenti vai labāka.

publiskā klase SpringRuleEngineTest paplašina TestCase {

public void testSuccessfulFlow () izmet izņēmumu {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication pieteikums = new LoanApplication (); application.setFirstName ("Jānis"); application.setLastName ("Doe"); application.setStateCode ("TX"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (pieteikums); assertEquals (LoanApplication.APPROVED, application.getStatus ()); } public void testInvalidState () izmet izņēmumu {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication pieteikums = new LoanApplication (); application.setFirstName ("Jānis"); application.setLastName ("Doe"); application.setStateCode ("OK"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (pieteikums); assertEquals (LoanApplication.INVALID_STATE, application.getStatus ()); } public void testInvalidRatio () izmet izņēmumu {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication pieteikums = new LoanApplication (); application.setFirstName ("Jānis"); application.setLastName ("Doe"); application.setStateCode ("MI"); application.setIncome (7000); application.setExpences (0,80 * 7000); // pārāk augsts dzinējs.processRequest (pieteikums); assertEquals (LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus ()); } public void testIncompleteApplication () izmet izņēmumu {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication pieteikums = new LoanApplication (); engine.processRequest (pieteikums); assertEquals (LoanApplication.INSUFFICIENT_DATA, application.getStatus ()); }

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