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

Java Tip 68: Μάθετε πώς να εφαρμόζετε το μοτίβο εντολών στην Java

Τα σχέδια σχεδίασης όχι μόνο επιταχύνουν τη φάση σχεδιασμού ενός αντικειμενοστρεφούς έργου (OO), αλλά επίσης αυξάνουν την παραγωγικότητα της ομάδας ανάπτυξης και την ποιότητα του λογισμικού. ΕΝΑ Μοτίβο εντολών είναι ένα πρότυπο συμπεριφοράς αντικειμένου που μας επιτρέπει να επιτύχουμε πλήρη αποσύνδεση μεταξύ του αποστολέα και του παραλήπτη. (ΕΝΑ αποστολέας είναι ένα αντικείμενο που επικαλείται μια λειτουργία, και a δέκτης είναι ένα αντικείμενο που λαμβάνει το αίτημα για εκτέλεση μιας συγκεκριμένης λειτουργίας. Με αποσύνδεση, ο αποστολέας δεν γνωρίζει το Δέκτηςδιεπαφή.) Ο όρος αίτηση εδώ αναφέρεται στην εντολή που πρόκειται να εκτελεστεί. Το μοτίβο εντολών μας επιτρέπει επίσης να αλλάξουμε πότε και πώς ικανοποιείται ένα αίτημα. Επομένως, ένα μοτίβο εντολών μας παρέχει ευελιξία καθώς και επεκτασιμότητα.

Σε γλώσσες προγραμματισμού όπως το C, δείκτες λειτουργίας χρησιμοποιούνται για την εξάλειψη των τεράστιων δηλώσεων μεταγωγής. (Ανατρέξτε στην ενότητα "Συμβουλή Java 30: Πολυμορφισμός και Java" για μια πιο λεπτομερή περιγραφή.) Επειδή η Java δεν έχει δείκτες λειτουργίας, μπορούμε να χρησιμοποιήσουμε το μοτίβο εντολών για να εφαρμόσουμε επιστροφές κλήσεων. Αυτό θα το δείτε σε δράση στο πρώτο παράδειγμα κώδικα παρακάτω, που ονομάζεται TestCommand.java.

Οι προγραμματιστές που έχουν συνηθίσει να χρησιμοποιούν δείκτες λειτουργίας σε άλλη γλώσσα μπορεί να μπουν στον πειρασμό να χρησιμοποιήσουν το Μέθοδος αντικείμενα του Reflection API με τον ίδιο τρόπο. Για παράδειγμα, στο άρθρο του "Java Reflection", ο Paul Tremblett σάς δείχνει πώς να χρησιμοποιήσετε το Reflection για να εκτελέσετε συναλλαγές χωρίς να χρησιμοποιήσετε εναλλαγές δηλώσεων. Έχω αντισταθεί σε αυτόν τον πειρασμό, καθώς η Sun προτείνει να μην χρησιμοποιήσετε το Reflection API όταν αρκούν άλλα εργαλεία πιο φυσικά στη γλώσσα προγραμματισμού Java. (Δείτε πόρους για συνδέσμους προς το άρθρο του Tremblett και για τη σελίδα φροντιστηρίου της Sun's Reflection.) Το πρόγραμμά σας θα είναι πιο εύκολο να εντοπιστεί και να διατηρηθεί εάν δεν το χρησιμοποιήσετε Μέθοδος αντικείμενα. Αντ 'αυτού, πρέπει να ορίσετε μια διεπαφή και να την εφαρμόσετε στις κλάσεις που εκτελούν την απαιτούμενη ενέργεια.

Επομένως, σας προτείνω να χρησιμοποιήσετε το μοτίβο εντολών σε συνδυασμό με το δυναμικό μηχανισμό φόρτωσης και δέσμευσης της Java για να εφαρμόσετε δείκτες λειτουργίας. (Για λεπτομέρειες σχετικά με τον δυναμικό μηχανισμό φόρτωσης και δέσμευσης της Java, ανατρέξτε στην ενότητα "The Java Language Environment - A White Paper" του James Gosling και του Henry McGilton).

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

Το μοτίβο εντολών μετατρέπει το ίδιο το αίτημα σε αντικείμενο. Αυτό το αντικείμενο μπορεί να αποθηκευτεί και να μεταδοθεί όπως άλλα αντικείμενα. Το κλειδί για αυτό το μοτίβο είναι ένα Εντολή διεπαφή, η οποία δηλώνει μια διεπαφή για την εκτέλεση λειτουργιών. Στην απλούστερη μορφή του, αυτή η διεπαφή περιλαμβάνει μια περίληψη εκτέλεση λειτουργία. Κάθε σκυρόδεμα Εντολή Η κλάση καθορίζει ένα ζεύγος δέκτη-δράσης αποθηκεύοντας το Δέκτης ως μεταβλητή παρουσίας. Παρέχει διαφορετικές εφαρμογές του εκτέλεση() μέθοδος επίκλησης του αιτήματος. ο Δέκτης έχει τις γνώσεις που απαιτούνται για την εκτέλεση του αιτήματος.

Το σχήμα 1 παρακάτω δείχνει το Διακόπτης - ένα σύνολο Εντολή αντικείμενα. Εχει flipUp () και flipDown () λειτουργίες στη διεπαφή του. Διακόπτης ονομάζεται εισβολέας επειδή επικαλείται τη λειτουργία εκτέλεσης στη διεπαφή εντολών.

Η συγκεκριμένη εντολή, Ελαφριά εντολή, εφαρμόζει το εκτέλεση λειτουργία της διεπαφής εντολών. Έχει τη γνώση να καλέσει τον κατάλληλο Δέκτης λειτουργία του αντικειμένου. Λειτουργεί ως προσαρμογέας σε αυτήν την περίπτωση. Με τον όρο προσαρμογέας, Εννοώ ότι το σκυρόδεμα Εντολή αντικείμενο είναι ένας απλός σύνδεσμος, που συνδέει το Invoker και το Δέκτης με διαφορετικές διεπαφές.

Ο πελάτης δημιουργεί το Invoker, ο Δέκτης, και τα συγκεκριμένα αντικείμενα εντολών.

Το σχήμα 2, το διάγραμμα ακολουθίας, δείχνει τις αλληλεπιδράσεις μεταξύ των αντικειμένων. Απεικονίζει πώς Εντολή αποσυνδέει το Invoker από το Δέκτης (και το αίτημα που εκτελείται). Ο πελάτης δημιουργεί μια συγκεκριμένη εντολή παραμετροποιώντας τον κατασκευαστή του με το κατάλληλο Δέκτης. Στη συνέχεια αποθηκεύει το Εντολή στο Invoker. ο Invoker καλεί πίσω τη συγκεκριμένη εντολή, η οποία έχει τη γνώση για την εκτέλεση του επιθυμητού Δράση() λειτουργία.

Ο πελάτης (κύριο πρόγραμμα στη λίστα) δημιουργεί ένα συγκεκριμένο Εντολή αντικείμενο και ορίζει το Δέκτης. Ως Invoker αντικείμενο, Διακόπτης αποθηκεύει το σκυρόδεμα Εντολή αντικείμενο. ο Invoker εκδίδει ένα αίτημα καλώντας εκτέλεση στο Εντολή αντικείμενο. Το σκυρόδεμα Εντολή αντικείμενο επικαλείται λειτουργίες σε αυτό Δέκτης για την εκτέλεση του αιτήματος.

Η βασική ιδέα εδώ είναι ότι η συγκεκριμένη εντολή εγγράφεται με το Invoker και το Invoker το καλεί πίσω, εκτελώντας την εντολή στο Δέκτης.

Παράδειγμα κώδικα εντολών εντολής

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

Το παράδειγμα δείχνει ένα Ανεμιστήρας και ένα Φως. Στόχος μας είναι να αναπτύξουμε ένα Διακόπτης που μπορεί να ενεργοποιήσει ή να απενεργοποιήσει οποιοδήποτε αντικείμενο. Βλέπουμε ότι το Ανεμιστήρας και το Φως έχουν διαφορετικές διεπαφές, που σημαίνει το Διακόπτης πρέπει να είναι ανεξάρτητο από το Δέκτης διεπαφή ή δεν γνωρίζει τον κωδικό> Διεπαφή παραλήπτη. Για να λύσουμε αυτό το πρόβλημα, πρέπει να παραμετροποιήσουμε κάθε ένα από τα Διακόπτηςs με την κατάλληλη εντολή. Προφανώς, το Διακόπτης συνδεδεμένο με το Φως θα έχει διαφορετική εντολή από το Διακόπτης συνδεδεμένο με το Ανεμιστήρας. ο Εντολή η τάξη πρέπει να είναι αφηρημένη ή μια διεπαφή για να λειτουργήσει αυτό.

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

Οταν ο flipUp () και flipDown () οι λειτουργίες καλούνται, θα κάνουν απλά την κατάλληλη εντολή εκτέλεση( ). ο Διακόπτης δεν θα έχει ιδέα τι συμβαίνει ως αποτέλεσμα εκτέλεση( ) καλείται.

TestCommand.java Fan class {public void startRotate () {System.out.println ("Fan is rotating"); } public void stopRotate () {System.out.println ("Ο ανεμιστήρας δεν περιστρέφεται"); }} κλάση Light {public void turnOn () {System.out.println ("Το φως είναι αναμμένο"); } public void turnOff () {System.out.println ("Το φως είναι απενεργοποιημένο"); }} κλάση διακόπτη {private Command UpCommand, DownCommand; δημόσιος διακόπτης (Command Up, Command Down) {UpCommand = Up; // η συγκεκριμένη εντολή εγγράφεται στον επενδυτή DownCommand = Down; } void flipUp () {// ο invoker καλεί συγκεκριμένη εντολή, η οποία εκτελεί την εντολή στο δέκτη UpCommand. εκτέλεση ( ) ; } void flipDown () {DownCommand. εκτέλεση ( ); }} Η κλάση LightOnCommand εφαρμόζει την εντολή {private Light myLight; δημόσια LightOnCommand (Light L) {myLight = L; } δημόσια άκυρη εκτέλεση () {myLight. ανάβω( ); }} Η κλάση LightOffCommand εφαρμόζει την εντολή {private Light myLight; δημόσιο LightOffCommand (Light L) {myLight = L; } δημόσια άκυρη εκτέλεση () {myLight. σβήνω( ); }} Η κατηγορία FanOnCommand εφαρμόζει την εντολή {private Fan myFan; δημόσια FanOnCommand (Fan F) {myFan = F; } δημόσια άκυρη εκτέλεση () {myFan. startRotate (); }} Η κατηγορία FanOffCommand εφαρμόζει την εντολή {private Fan myFan; κοινό FanOffCommand (Fan F) {myFan = F; } δημόσια άκυρη εκτέλεση () {myFan. stopRotate (); }} δημόσια τάξη TestCommand {public static void main (String [] args) {Light testLight = new Light (); LightOnCommand testLOC = νέο LightOnCommand (testLight); LightOffCommand testLFC = νέο LightOffCommand (testLight); Διακόπτης testSwitch = νέος διακόπτης (testLOC, testLFC); testSwitch.flipUp (); testSwitch.flipDown (); Fan testFan = νέος ανεμιστήρας (); FanOnCommand foc = νέο FanOnCommand (testFan); FanOffCommand ffc = νέο FanOffCommand (testFan); Διακόπτης ts = νέος διακόπτης (foc, ffc); ts.flipUp (); ts.flipDown (); }} Command.java δημόσια διεπαφή Command {public abstract void execute (); } 

Παρατηρήστε στο παραπάνω παράδειγμα κώδικα ότι το μοτίβο εντολών αποσυνδέει πλήρως το αντικείμενο που επικαλείται τη λειτουργία - (Διακόπτης ) - από αυτούς που έχουν τη γνώση να το εκτελέσουν - Φως και Ανεμιστήρας. Αυτό μας δίνει μεγάλη ευελιξία: το αντικείμενο που υποβάλλει ένα αίτημα πρέπει να γνωρίζει μόνο πώς να το εκδώσει. δεν χρειάζεται να γνωρίζει πώς θα εκτελεστεί το αίτημα.

Μοτίβο εντολών για την πραγματοποίηση συναλλαγών

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

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

Στον κωδικό πελάτη του προγράμματος TestTransactionCommand.java, όλα τα αιτήματα ενσωματώνονται στο γενικό Εντολή συναλλαγής αντικείμενο. ο Εντολή συναλλαγής ο κατασκευαστής δημιουργείται από τον πελάτη και είναι εγγεγραμμένος στο CommandManager. Τα αιτήματα στην ουρά μπορούν να εκτελεστούν σε διαφορετικούς χρόνους καλώντας το runCommands (), που μας δίνει μεγάλη ευελιξία. Μας δίνει επίσης τη δυνατότητα συναρμολόγησης εντολών σε σύνθετη εντολή. έχω επίσης Όργανο εντολών, CommandReceiver, και CommandManager τάξεις και υποκατηγορίες του Εντολή συναλλαγής -- και συγκεκριμένα Προσθήκη εντολής και Αφαιρέστε την εντολή. Ακολουθεί μια περιγραφή καθεμιάς από αυτές τις τάξεις:

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

  • CommandReceiver εφαρμόζει όλες τις μεθόδους επεξεργασίας εντολών και εφαρμόζεται ως μοτίβο Singleton.

  • CommandManager είναι ο εισβολέας και είναι το Διακόπτης ισοδύναμο με το προηγούμενο παράδειγμα. Αποθηκεύει το γενικό Εντολή συναλλαγής αντικείμενο στο ιδιωτικό του εντολή μου μεταβλητός. Πότε runCommands () καλείται, καλεί το εκτέλεση( ) του κατάλληλου Εντολή συναλλαγής αντικείμενο.

Στην Java, είναι δυνατό να αναζητήσετε τον ορισμό μιας κλάσης με μια συμβολοσειρά που περιέχει το όνομά της. Στο εκτέλεση ( ) λειτουργία του Εντολή συναλλαγής class, υπολογίζω το όνομα της κλάσης και το συνδέω δυναμικά στο τρέχον σύστημα - δηλαδή, τα μαθήματα φορτώνονται εν κινήσει όπως απαιτείται. Χρησιμοποιώ τη σύμβαση ονομασίας, το όνομα εντολής που συνδυάζεται με τη συμβολοσειρά "Command" ως το όνομα της υποκατηγορίας εντολών συναλλαγής, έτσι ώστε να μπορεί να φορτωθεί δυναμικά.

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