Προγραμματισμός

Προσθέστε μια απλή μηχανή κανόνα στις εφαρμογές σας που βασίζονται στην άνοιξη

Οποιοδήποτε μη εμπιστευτικό πρόγραμμα λογισμικού περιέχει ένα ασήμαντο ποσό της λεγόμενης επιχειρηματικής λογικής. Αυτό που ακριβώς αποτελεί επιχειρηματική λογική είναι συζητήσιμο. Στα βουνά του κώδικα που παράγεται για μια τυπική εφαρμογή λογισμικού, κομμάτια εδώ και εκεί πραγματικά κάνουν τη δουλειά για την οποία ζητήθηκε το λογισμικό - επεξεργασία παραγγελιών, έλεγχος όπλων, σχεδίαση φωτογραφιών κ.λπ. , καταγραφή, συναλλαγές, περίεργες γλώσσες, ιδιομορφίες πλαισίου και άλλα στοιχεία μιας σύγχρονης εταιρικής εφαρμογής.

Τις περισσότερες φορές, η επιχειρηματική λογική συνδυάζεται βαθιά με όλα αυτά τα άλλα κομμάτια. Όταν χρησιμοποιούνται βαριά, ενοχλητικά πλαίσια (όπως το Enterprise JavaBeans), η διάκριση από πού τελειώνει η επιχειρηματική λογική και ο κώδικας που εμπνέεται από το πλαίσιο γίνεται ιδιαίτερα δύσκολος.

Υπάρχει μια απαίτηση λογισμικού που σπάνια αναφέρεται στα έγγραφα ορισμού απαιτήσεων, αλλά έχει τη δύναμη να κάνει ή να σπάσει οποιοδήποτε έργο λογισμικού: προσαρμοστικότητα, το μέτρο του πόσο εύκολο είναι να αλλάξετε το λογισμικό σε απάντηση σε αλλαγές στο επιχειρηματικό περιβάλλον.

Οι σύγχρονες εταιρείες αναγκάζονται να είναι γρήγορες και ευέλικτες και θέλουν το ίδιο από το εταιρικό τους λογισμικό. Οι επιχειρηματικοί κανόνες που εφαρμόστηκαν τόσο προσεκτικά στην επιχειρηματική λογική των τάξεων σας σήμερα θα καταστούν παρωχημένοι αύριο και θα πρέπει να αλλάξουν γρήγορα και με ακρίβεια. Όταν ο κώδικάς σας έχει επιχειρηματική λογική θαμμένη βαθιά μέσα σε τόνους και τόνους αυτών των άλλων bit, η τροποποίηση θα γίνει γρήγορα αργή, επώδυνη και επιρρεπής σε σφάλματα.

Δεν υπάρχει αμφιβολία ότι μερικά από τα πιο μοντέρνα πεδία στο λογισμικό των επιχειρήσεων σήμερα είναι μηχανές κανόνα και διάφορα συστήματα διαχείρισης επιχειρησιακών διαδικασιών (BPM). Μόλις κοιτάξετε μέσα από το μάρκετινγκ, αυτά τα εργαλεία υπόσχονται ουσιαστικά το ίδιο πράγμα: το Holy Grail of Business Logic που συλλαμβάνεται σε ένα αποθετήριο, καθαρά διαχωρισμένο και υφιστάμενο από μόνο του, έτοιμο να κληθεί από οποιαδήποτε εφαρμογή που έχετε στο σπίτι του λογισμικού.

Αν και οι εμπορικοί κινητήρες κανόνα και τα συστήματα BPM έχουν πολλά πλεονεκτήματα, περιλαμβάνουν επίσης πολλές ελλείψεις. Το εύκολο να διαλέξετε είναι η τιμή, η οποία μερικές φορές μπορεί εύκολα να φτάσει στα επτά ψηφία. Ένα άλλο είναι η έλλειψη πρακτικής τυποποίησης που συνεχίζεται σήμερα παρά τις σημαντικές προσπάθειες της βιομηχανίας και τα πολλά διαθέσιμα πρότυπα σε χαρτί. Και, καθώς όλο και περισσότερα καταστήματα λογισμικού προσαρμόζουν ευέλικτες, αδύνατες και γρήγορες μεθοδολογίες ανάπτυξης, αυτά τα εργαλεία βαρέων βαρών δυσκολεύονται να χωρέσουν.

Σε αυτό το άρθρο, χτίζουμε μια απλή μηχανή κανόνα που, από τη μία πλευρά, αξιοποιεί τον σαφή διαχωρισμό της επιχειρηματικής λογικής που είναι τυπική για τέτοια συστήματα και, από την άλλη πλευρά - επειδή υποστηρίζεται από το δημοφιλές και ισχυρό πλαίσιο J2EE - δεν υποφέρετε από την πολυπλοκότητα και την «ψυχραιμία» των εμπορικών προσφορών.

Άνοιξη στον κόσμο του J2EE

Αφού η πολυπλοκότητα του εταιρικού λογισμικού έγινε αφόρητη και το πρόβλημα της επιχειρησιακής λογικής μπήκε στο προσκήνιο, γεννήθηκαν το Spring Framework και άλλα παρόμοια. Αναμφισβήτητα, η Άνοιξη είναι το καλύτερο πράγμα που συνέβη στην επιχείρηση Java για πολύ καιρό. Το Spring παρέχει τη μεγάλη λίστα εργαλείων και μικρών ευκολιών κώδικα που κάνουν τον προγραμματισμό J2EE πιο αντικειμενοστρεφής, πολύ πιο εύκολο και, πολύ καλύτερα, πιο διασκεδαστικό.

Στην καρδιά της Άνοιξης βρίσκεται η αρχή της Αντιστροφής του Ελέγχου. Αυτό είναι ένα φανταχτερό και υπερφορτωμένο όνομα, αλλά καταλήγει σε αυτές τις απλές ιδέες:

  • Η λειτουργικότητα του κωδικού σας χωρίζεται σε μικρά διαχειρίσιμα κομμάτια
  • Αυτά τα κομμάτια αντιπροσωπεύονται από απλά, τυπικά φασόλια Java (απλές τάξεις Java που παρουσιάζουν ορισμένες, αλλά όχι όλες, τις προδιαγραφές JavaBeans)
  • Κάνεις δεν ασχοληθείτε με τη διαχείριση αυτών των φασολιών (δημιουργία, καταστροφή, ρύθμιση εξαρτήσεων)
  • Αντ 'αυτού, το δοχείο Spring το κάνει για εσάς βάσει ορισμένων ορισμός περιβάλλοντος συνήθως παρέχεται με τη μορφή αρχείου XML

Το Spring παρέχει επίσης πολλές άλλες δυνατότητες, όπως ένα πλήρες και ισχυρό πλαίσιο Model-View-Controller για εφαρμογές Web, περιτυλίγματα ευκολίας για προγραμματισμό Java Database Connectivity και δώδεκα άλλα πλαίσια. Αλλά αυτά τα θέματα φτάνουν πολύ έξω από το πεδίο εφαρμογής αυτού του άρθρου.

Πριν περιγράψω τι χρειάζεται για τη δημιουργία ενός απλού μηχανισμού κανόνα για εφαρμογές με βάση την άνοιξη, ας εξετάσουμε γιατί αυτή η προσέγγιση είναι καλή ιδέα.

Τα σχέδια κινητήρα κανόνα έχουν δύο ενδιαφέρουσες ιδιότητες που τα κάνουν αξιόλογα:

  • Πρώτον, διαχωρίζουν τον επιχειρηματικό λογικό κώδικα από άλλους τομείς της εφαρμογής
  • Δεύτερον, είναι εξωτερικά διαμορφώσιμο, που σημαίνει ότι οι ορισμοί των επιχειρηματικών κανόνων και πώς και με ποια σειρά ενεργοποιούνται αποθηκεύονται εξωτερικά στην εφαρμογή και χειρίζονται από τον δημιουργό κανόνων, όχι από τον χρήστη της εφαρμογής ή ακόμη και από έναν προγραμματιστή

Το ελατήριο παρέχει μια καλή εφαρμογή για έναν κινητήρα κανόνα. Ο εξαιρετικά συστατικός σχεδιασμός μιας σωστά κωδικοποιημένης εφαρμογής Spring προωθεί την τοποθέτηση του κώδικα σας σε μικρό, διαχειρίσιμο, ξεχωριστός κομμάτια (φασόλια), τα οποία διαμορφώνονται εξωτερικά μέσω των ορισμών περιβάλλοντος της Άνοιξης.

Διαβάστε παρακάτω για να εξερευνήσετε αυτήν την καλή αντιστοιχία ανάμεσα σε αυτό που χρειάζεται ένας σχεδιασμός κινητήρα κανόνα και αυτό που ήδη παρέχει ο σχεδιασμός Spring.

Ο σχεδιασμός ενός κινητήρα κανόνα με ελατήριο

Βασίζουμε το σχέδιό μας στην αλληλεπίδραση των φασολιών Java που ελέγχονται από την άνοιξη, την οποία ονομάζουμε εξαρτήματα κινητήρα κανόνα. Ας καθορίσουμε τους δύο τύπους στοιχείων που ίσως χρειαζόμαστε:

  • Ενα δράση είναι ένα στοιχείο που κάνει κάτι χρήσιμο στη λογική της εφαρμογής μας
  • ΕΝΑ κανόνας είναι ένα συστατικό που κάνει ένα απόφαση σε μια λογική ροή δράσεων

Καθώς είμαστε μεγάλοι οπαδοί του καλού αντικειμενοστρεφούς σχεδιασμού, η ακόλουθη βασική κατηγορία αποτυπώνει τη βασική λειτουργικότητα όλων των συστατικών μας που έρχονται, δηλαδή, τη δυνατότητα να κληθούν από άλλα στοιχεία με κάποιο επιχείρημα:

δημόσια αφηρημένη κλάση AbstractComponent {public abstract void execute (Object arg) ρίχνει Exception; }

Φυσικά η βασική τάξη είναι αφηρημένη γιατί δεν θα χρειαζόμαστε ποτέ από μόνη της.

Και τώρα, κωδικοποιήστε για ένα ΠερίληψηΔράση, να επεκταθεί με άλλες μελλοντικές συγκεκριμένες δράσεις:

δημόσια αφηρημένη τάξη AbstractAction επεκτείνει το AbstractComponent {

ιδιωτικό AbstractComponent nextStep; public void execute (Object arg) ρίχνει την Εξαίρεση {this.doExecute (arg); εάν (nextStep! = null) nextStep.execute (arg); } το προστατευμένο abstract void doExecute (Object arg) ρίχνει την Εξαίρεση.

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

δημόσια AbstractComponent getNextStep () {return nextStep; }

}

Οπως βλέπεις, ΠερίληψηΔράση κάνει δύο πράγματα: Αποθηκεύει τον ορισμό του επόμενου στοιχείου που θα καλείται από τη μηχανή κανόνα μας. Και, σε αυτό εκτέλεση() μέθοδος, καλεί a doExecute () μέθοδος που θα καθοριστεί από συγκεκριμένη υποκατηγορία. Μετά doExecute () επιστρέφει, το επόμενο στοιχείο καλείται εάν υπάρχει.

Μας Περίληψη είναι εξίσου απλό:

δημόσια αφηρημένη κλάση AbstractRule επεκτείνει το AbstractComponent {

ιδιωτικό AbstractComponent positiveOutcomeStep; ιδιωτικό AbstractComponent negativeOutcomeStep; public void execute (Object arg) ρίχνει την Εξαίρεση {boolean outcome = makeDecision (arg); εάν (αποτέλεσμα) positiveOutcomeStep.execute (arg); άλλο αρνητικόOutcomeStep.execute (arg);

}

Το προστατευμένο abstract boolean makeDecision (Object arg) ρίχνει την Εξαίρεση.

// Οι λήπτες και οι ρυθμιστές για το positiveOutcomeStep και το αρνητικόOutcomeStep παραλείπονται για συντομία

Μέσα στο εκτέλεση() μέθοδος, το ΠερίληψηΔράση καλεί το Πάρε απόφαση() μέθοδος, την οποία μια υποκατηγορία εφαρμόζει και, στη συνέχεια, ανάλογα με το αποτέλεσμα αυτής της μεθόδου, καλεί ένα από τα στοιχεία που ορίζονται ως θετικά ή αρνητικά αποτελέσματα.

Ο σχεδιασμός μας είναι πλήρης όταν το παρουσιάσουμε SpringRuleEngine τάξη:

δημόσια τάξη SpringRuleEngine {private AbstractComponent firstStep; public void setFirstStep (AbstractComponent firstStep) {this.firstStep = firstStep; } public void processRequest (Object arg) ρίχνει την Εξαίρεση {firstStep.execute (arg); }}

Αυτό είναι το μόνο που υπάρχει στην κύρια κατηγορία της μηχανής κανόνα μας: ο ορισμός ενός πρώτου στοιχείου στην επιχειρηματική μας λογική και η μέθοδος για την έναρξη της επεξεργασίας.

Αλλά περιμένετε, πού είναι τα υδραυλικά που ενώνουν όλες τις τάξεις μας ώστε να μπορούν να λειτουργήσουν; Στη συνέχεια θα δείτε πώς η μαγεία της Άνοιξης μας βοηθά με αυτό το έργο.

Κινητήρας κανόνα με ελατήριο σε δράση

Ας δούμε ένα συγκεκριμένο παράδειγμα του τρόπου λειτουργίας αυτού του πλαισίου. Εξετάστε αυτήν την περίπτωση χρήσης: πρέπει να αναπτύξουμε μια εφαρμογή υπεύθυνη για την επεξεργασία αιτήσεων δανείου. Πρέπει να ικανοποιήσουμε τις ακόλουθες απαιτήσεις:

  • Ελέγχουμε την αίτηση για πληρότητα και την απορρίπτουμε διαφορετικά
  • Ελέγχουμε εάν η αίτηση προήλθε από έναν αιτούντα που ζει σε μια πολιτεία όπου έχουμε εξουσιοδότηση για επιχειρηματική δραστηριότητα
  • Ελέγχουμε αν το μηνιαίο εισόδημα του αιτούντος και τα μηνιαία έξοδά του ταιριάζουν σε μια αναλογία με την οποία αισθανόμαστε άνετα
  • Οι εισερχόμενες εφαρμογές αποθηκεύονται σε μια βάση δεδομένων μέσω μιας υπηρεσίας επιμονής για την οποία δεν γνωρίζουμε τίποτα, εκτός από τη διεπαφή της (ίσως η ανάπτυξή της ανατέθηκε στην Ινδία)
  • Οι επιχειρηματικοί κανόνες υπόκεινται σε αλλαγές, και αυτός είναι ο λόγος για τον οποίο απαιτείται σχεδιασμός μηχανών κανόνα

Αρχικά, ας σχεδιάσουμε μια τάξη που αντιπροσωπεύει την αίτηση δανείου μας:

δημόσια τάξη LoanApplication {public static final String INVALID_STATE = "Συγγνώμη δεν κάνουμε επιχειρήσεις στην πολιτεία σας"; δημόσια στατική τελική συμβολοσειρά INVALID_INCOME_EXPENSE_RATIO = "Δυστυχώς δεν μπορούμε να παρέχουμε το δάνειο δεδομένης αυτής της αναλογίας δαπανών / εσόδων"; public static final String APPROVED = "Η αίτησή σας έχει εγκριθεί"; public static final String INSUFFICIENT_DATA = "Δεν παρείχατε αρκετές πληροφορίες για την αίτησή σας"; public static final String INPROGRESS = "σε εξέλιξη"; δημόσια στατική τελική συμβολοσειρά [] STATUSES = νέα συμβολοσειρά [] {INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, ΕΓΚΕΚΡΙΜΕΝΗ, INPROGRESS};

ιδιωτικό όνομα συμβολοσειράς; ιδιωτική συμβολοσειρά επώνυμο; ιδιωτικό διπλό εισόδημα ιδιωτικές διπλές εκδηλώσεις ιδιωτικό String stateCode; ιδιωτική κατάσταση συμβολοσειράς; public void setStatus (String status) {if (! Arrays.asList (STATUSES) .contains (status)) ρίξτε νέο IllegalArgumentException ("μη έγκυρη κατάσταση:" + κατάσταση); this.status = κατάσταση; }

// Παραλείπονται δεκάδες άλλοι παραλήπτες και ρυθμιστές

}

Η δεδομένη υπηρεσία εμμονής μας περιγράφεται από την ακόλουθη διεπαφή:

δημόσια διεπαφή LoanApplicationPersistenceInterface {public void recordApproval (LoanApplication application) ρίχνει την Εξαίρεση. public void recordRejection (εφαρμογή LoanApplication) ρίχνει την εξαίρεση. public void recordIncomplete (εφαρμογή LoanApplication) ρίχνει την εξαίρεση. }

Χλευάζουμε γρήγορα αυτήν τη διεπαφή αναπτύσσοντας ένα MockLoanApplication Επιμονή κλάση που δεν κάνει τίποτα παρά να ικανοποιεί τη σύμβαση που ορίζεται από τη διεπαφή.

Χρησιμοποιούμε την ακόλουθη υποκατηγορία του SpringRuleEngine τάξη για να φορτώσετε το πλαίσιο Spring από ένα αρχείο XML και να ξεκινήσετε πραγματικά την επεξεργασία:

δημόσια τάξη LoanProcessRuleEngine επεκτείνει το SpringRuleEngine {public static final SpringRuleEngine getEngine (όνομα συμβολοσειράς) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("SpringRuleEngineContext.xml"); return (SpringRuleEngine) context.getBean (όνομα); }}

Αυτή τη στιγμή, έχουμε τον σκελετό στη θέση του, οπότε είναι η ιδανική στιγμή για να γράψουμε ένα τεστ JUnit, το οποίο εμφανίζεται παρακάτω. Έχουν γίνει ορισμένες παραδοχές: Περιμένουμε ότι η εταιρεία μας θα λειτουργεί μόνο σε δύο πολιτείες, το Τέξας και το Μίσιγκαν. Δεχόμαστε μόνο δάνεια με αναλογία δαπανών / εσόδων 70% ή καλύτερα.

δημόσια τάξη SpringRuleEngineTest επεκτείνει το TestCase {

public void testSuccessfulFlow () ρίχνει την εξαίρεση {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = νέο LoanApplication (); application.setFirstName ("John"); application.setLastName ("Doe"); application.setStateCode ("TX"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (εφαρμογή); assertEquals (LoanApplication.APPROVED, application.getStatus ()); } public void testInvalidState () ρίχνει την εξαίρεση {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = νέο LoanApplication (); application.setFirstName ("John"); application.setLastName ("Doe"); application.setStateCode ("ΟΚ"); application.setExpences (4500); application.setIncome (7000); engine.processRequest (εφαρμογή); assertEquals (LoanApplication.INVALID_STATE, application.getStatus ()); } public void testInvalidRatio () ρίχνει την εξαίρεση {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = νέο LoanApplication (); application.setFirstName ("John"); application.setLastName ("Doe"); application.setStateCode ("MI"); application.setIncome (7000); application.setExpences (0,80 * 7000); // πολύ υψηλό engine.processRequest (εφαρμογή); assertEquals (LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus ()); } public void testIncompleteApplication () ρίχνει την εξαίρεση {SpringRuleEngine engine = LoanProcessRuleEngine.getEngine ("SharkysExpressLoansApplicationProcessor"); LoanApplication application = νέο LoanApplication (); engine.processRequest (εφαρμογή); assertEquals (LoanApplication.INSUFFICIENT_DATA, application.getStatus ()); }