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

Σχεδιασμός πεδίων και μεθόδων

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

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

Σχεδιασμός πεδίων

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

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

  • Η ποσότητα του καφέ που περιέχει το φλιτζάνι
  • Είτε το κύπελλο είναι καθαρό είτε βρώμικο

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

Εάν αποφασίσετε ότι ενδιαφέρεστε μόνο εάν ένα ποτήρι έχει πλυθεί ή όχι εάν είναι άδειο, θα μπορούσατε να χρησιμοποιήσετε μια ειδική τιμή του καφές πεδίο, το οποίο χρησιμοποιείται συνήθως για να παρακολουθεί την ποσότητα του καφέ στο κύπελλο, για να αντιπροσωπεύει ένα άπλυτο κύπελλο. Εάν 473 χιλιοστόλιτρα (16 ουγκιές υγρού) είναι η μέγιστη ποσότητα καφέ στο μεγαλύτερο φλιτζάνι σας, τότε η μέγιστη τιμή καφές κανονικά θα ήταν 473. Έτσι, θα μπορούσατε να χρησιμοποιήσετε ένα καφές τιμή, ας πούμε, 500 (μια ειδική τιμή) για να δείξει ένα άδειο φλιτζάνι που δεν πλένεται:

// Στο πακέτο προέλευσης στα πεδία αρχείων / ex1 / CoffeeCup.java class CoffeeCup {private int innerCoffee; public boolean isReadyForNextUse () {// Εάν το φλιτζάνι καφέ δεν πλυθεί, τότε // δεν είναι έτοιμο για επόμενη χρήση εάν (innerCoffee == 500) {return false; } επιστροφή αληθινή; } public void setCustomerDone () {innerCoffee = 500; // ...} δημόσιο άδειο πλύσιμο () {innerCoffee = 0; // ...} // ...} 

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

Για παράδειγμα, εάν αργότερα κάποιος προσθέσει ένα φλιτζάνι 20 ουγκιών στις προσφορές του εικονικού καφέ, τότε θα ήταν δυνατό να κρατήσετε έως και 592 χιλιοστόλιτρα (ml) καφέ σε ένα φλιτζάνι. Εάν ένας προγραμματιστής προσθέσει το νέο μέγεθος του κυπέλλου χωρίς να συνειδητοποιήσει ότι χρησιμοποιείτε 500 ml για να δείξετε ότι ένα κύπελλο χρειάζεται πλύσιμο, είναι πιθανό να εισαχθεί ένα σφάλμα. Εάν ένας πελάτης στο εικονικό σας καφέ αγόραζε ένα φλιτζάνι 20 ουγκιών και έπαιρνε ένα μεγάλο κόλπο 92 ml, τότε θα είχε στη διάθεσή του ακριβώς 500 ml στο κύπελλο. Ο πελάτης θα ήταν σοκαρισμένος και δυσαρεστημένος όταν, αφού πίνει μόνο 92 ml, το κύπελλο εξαφανίστηκε από το χέρι του και εμφανίστηκε στο νεροχύτη, έτοιμο να πλυθεί. Και, ακόμη και αν ο προγραμματιστής που έκανε την αλλαγή συνειδητοποίησε ότι χρησιμοποιούσατε μια ειδική τιμή, θα έπρεπε να επιλεγεί μια άλλη ειδική τιμή για το άπλυτο χαρακτηριστικό.

Μια καλύτερη προσέγγιση σε αυτήν την κατάσταση είναι να έχετε ένα ξεχωριστό πεδίο για να διαμορφώσετε το ξεχωριστό χαρακτηριστικό:

// Στο πακέτο προέλευσης στα πεδία αρχείων / ex2 / CoffeeCup.java class CoffeeCup {private int innerCoffee; ιδιωτικές boolean ανάγκεςΠλύσιμο; public boolean isReadyForNextUse () {// Εάν το φλιτζάνι καφέ δεν πλυθεί, τότε // δεν είναι έτοιμο για επιστροφή στην επόμενη χρήση! } public void setCustomerDone () {needsWashing = true; // ...} δημόσιο άδειο πλύσιμο () {needsWashing = false; // ...} // ...} 

Εδώ καφές Το πεδίο χρησιμοποιείται μόνο για τη μοντελοποίηση της ποσότητας καφέ στο χαρακτηριστικό cup. Το χαρακτηριστικό cup-needs-washing διαμορφώνεται από το χρειάζεται πλύσιμο πεδίο. Αυτό το σχήμα είναι πιο κατανοητό από το προηγούμενο σχήμα, το οποίο χρησιμοποίησε μια ειδική τιμή καφές και δεν θα εμπόδιζε κάποιον να επεκτείνει τη μέγιστη τιμή για καφές.

Χρήση σταθερών

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

εάν (cup.getSize () == CoffeeCup.TALL) {} 

από το να το καταλάβετε:

εάν (cup.getSize () == 1) {} 

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

Σταθερές και ο μεταγλωττιστής Java

Ένα χρήσιμο πράγμα που πρέπει να γνωρίζετε για τον μεταγλωττιστή Java είναι ότι αντιμετωπίζει στατικά τελικά πεδία (σταθερές) διαφορετικά από άλλα είδη πεδίων. Οι αναφορές σε στατικές τελικές μεταβλητές που έχουν αρχικοποιηθεί σε μια σταθερά χρόνου μεταγλώττισης επιλύονται κατά το χρόνο μεταγλώττισης σε ένα τοπικό αντίγραφο της σταθερής τιμής. Αυτό ισχύει για σταθερές όλων των πρωτόγονων τύπων και τύπων java.lang.String.

Κανονικά, όταν η τάξη σας αναφέρεται σε άλλη τάξη - ας πούμε, τάξη java.lang.Math - ο μεταγλωττιστής Java τοποθετεί συμβολικές αναφορές στην τάξη Μαθηματικά στο αρχείο τάξης για την τάξη σας. Για παράδειγμα, εάν μια μέθοδος της τάξης σας επικαλεστεί Math.sin (), το αρχείο τάξης θα περιέχει δύο συμβολικές αναφορές στο Μαθηματικά:

  • Μια συμβολική αναφορά στην τάξη Μαθηματικά
  • Μια συμβολική αναφορά στο Μαθηματικά'μικρό αμαρτία() μέθοδος

Για να εκτελέσετε τον κωδικό που περιέχεται στην τάξη σας στον οποίο αναφέρεται Math.sin (), το JVM θα πρέπει να φορτώσει την τάξη Μαθηματικά για να επιλύσετε τις συμβολικές αναφορές.

Αν, από την άλλη πλευρά, ο κώδικάς σας αναφέρεται μόνο στη στατική τελική μεταβλητή κλάσης πι δηλώθηκε στην τάξη Μαθηματικά, ο μεταγλωττιστής Java δεν θα τοποθετούσε καμία συμβολική αναφορά Μαθηματικά στο αρχείο τάξης για την τάξη σας. Αντ 'αυτού, θα τοποθετούσε απλώς ένα αντίγραφο της κυριολεκτικής αξίας του Math.PI στο αρχείο τάξης της τάξης σας. Για να εκτελέσετε τον κωδικό που περιέχεται στην τάξη σας που χρησιμοποιεί το Math.PI σταθερή, το JVM δεν θα χρειαστεί να φορτώσει την τάξη Μαθηματικά.

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

Τρία είδη μεθόδων

Το υπόλοιπο αυτού του άρθρου θα συζητήσει τεχνικές σχεδιασμού μεθόδων που σχετίζονται με τα δεδομένα που χρησιμοποιεί ή τροποποιεί μια μέθοδος. Σε αυτό το πλαίσιο, θα ήθελα να προσδιορίσω και να ονομάσω τρεις βασικούς τύπους μεθόδων σε προγράμματα Java: το μέθοδος χρησιμότητας ο μέθοδος προβολής κατάστασης, και το μέθοδος αλλαγής κατάστασης.

Η μέθοδος χρησιμότητας

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

Μερικά παραδείγματα μεθόδων χρησιμότητας από το Java API είναι:

  • (Στην τάξη Ακέραιος αριθμός) δημόσιο στατικό int toString (int i) - επιστρέφει ένα νέο Σειρά αντικείμενο που αντιπροσωπεύει τον καθορισμένο ακέραιο σε ακτίνα 10
  • (Στην τάξη Μαθηματικά) δημόσιο στατικό εγγενές διπλό cos (διπλό α) - επιστρέφει το τριγωνομετρικό συνημίτονο μιας γωνίας

Η μέθοδος προβολής κατάστασης

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

Μερικά παραδείγματα μεθόδων προβολής κατάστασης από το Java API είναι:

  • (Στην τάξη Αντικείμενο) δημόσια συμβολοσειρά toString () - επιστρέφει μια παράσταση συμβολοσειράς του αντικειμένου
  • (Στην τάξη Ακέραιος αριθμός) δημόσιο byte byteValue () - επιστρέφει την τιμή του Ακέραιος αριθμός αντικείμενο ως byte
  • (Στην τάξη Σειρά) δημόσιο int indexOf (int ch) - επιστρέφει το ευρετήριο εντός της συμβολοσειράς της πρώτης εμφάνισης του καθορισμένου χαρακτήρα

Η μέθοδος αλλαγής κατάστασης

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

Μερικά παραδείγματα μεθόδων αλλαγής κατάστασης από το Java API είναι:

  • (Στην τάξη StringBuffer) δημόσιο προσάρτημα StringBuffer (int i) - προσαρτά την παράσταση συμβολοσειράς του int επιχείρημα προς το StringBuffer
  • (Στην τάξη Hashtable) δημόσιο συγχρονισμένο κενό κενό () - καθαρίζει το Hashtable έτσι ώστε να μην περιέχει κλειδιά
  • (Στην τάξη Διάνυσμα) δημόσιο τελικό συγχρονισμένο void addElement (Object obj) - προσθέτει το καθορισμένο στοιχείο στο τέλος του Διάνυσμα, αυξάνοντας το μέγεθός του κατά ένα

Ελαχιστοποίηση σύζευξης μεθόδου

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

Καθώς σχεδιάζετε μεθόδους, ένας από τους στόχους σας πρέπει να είναι η ελαχιστοποίηση σύζευξη - ο βαθμός αλληλεξάρτησης μεταξύ μιας μεθόδου και του περιβάλλοντος (άλλες μέθοδοι, αντικείμενα και τάξεις). Όσο λιγότερη σύζευξη υπάρχει μεταξύ μιας μεθόδου και του περιβάλλοντός της, τόσο πιο ανεξάρτητη είναι η μέθοδος και τόσο πιο ευέλικτη είναι η σχεδίαση.

Μέθοδοι ως μετασχηματιστές δεδομένων

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

Το Σχήμα 1 δείχνει μια γραφική απεικόνιση της μεθόδου ως μετασχηματιστής δεδομένων: Ένα διάγραμμα ροής δεδομένων από δομημένο (όχι αντικειμενικό) σχεδιασμό.

Είσοδος και έξοδος

Μια μέθοδος στην Java μπορεί να λάβει δεδομένα εισόδου από πολλές πηγές:

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

Ομοίως, μια μέθοδος μπορεί να εκφράσει την παραγωγή της σε πολλά μέρη:

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

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

Ελάχιστα συνδεδεμένες μέθοδοι χρησιμότητας

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

  1. Λαμβάνει είσοδο μόνο από τις παραμέτρους του
  2. Εκφράζει την παραγωγή του μόνο μέσω των παραμέτρων ή της τιμής επιστροφής (ή ρίχνοντας μια εξαίρεση)
  3. Δέχεται δεδομένα εισαγωγής μόνο που χρειάζονται πραγματικά από τη μέθοδο
  4. Επιστρέφει ως έξοδο μόνο δεδομένα που παράγονται πραγματικά με τη μέθοδο

Μια καλή μέθοδος χρησιμότητας

Για παράδειγμα, η μέθοδος μετατροπή OzToMl () φαίνεται παρακάτω αποδέχεται ένα int ως η μοναδική του είσοδος και επιστρέφει ένα int ως η μοναδική του παραγωγή:

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