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

Γιατί οι μέθοδοι getter και setter είναι κακές

Δεν σκόπευα να ξεκινήσω μια σειρά "είναι κακό", αλλά αρκετοί αναγνώστες μου ζήτησαν να εξηγήσω γιατί ανέφερα ότι πρέπει να αποφύγετε τις μεθόδους λήψης / ρύθμισης στη στήλη του περασμένου μήνα, "Γιατί επεκτείνεται είναι κακό".

Αν και οι μέθοδοι getter / setter είναι συνηθισμένες στην Java, δεν είναι ιδιαίτερα αντικειμενικές (OO). Στην πραγματικότητα, μπορούν να βλάψουν τη συντήρηση του κωδικού σας. Επιπλέον, η παρουσία πολλών μεθόδων λήψης και ρύθμισης είναι μια κόκκινη σημαία που το πρόγραμμα δεν είναι απαραίτητα καλά σχεδιασμένο από την οπτική γωνία.

Αυτό το άρθρο εξηγεί γιατί δεν πρέπει να χρησιμοποιείτε getter και setter (και πότε μπορείτε να τα χρησιμοποιήσετε) και προτείνει μια μεθοδολογία σχεδιασμού που θα σας βοηθήσει να ξεφύγετε από τη νοοτροπία getter / setter.

Σχετικά με τη φύση του σχεδιασμού

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

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

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

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

Το να δηλώνεις ότι κάποια γλωσσική δυνατότητα ή ένα κοινό ιδίωμα προγραμματισμού (όπως οι προσβάτες) έχει προβλήματα δεν είναι το ίδιο με το να πεις ότι δεν πρέπει ποτέ να τα χρησιμοποιείς σε καμία περίπτωση. Και επειδή το χαρακτηριστικό ή το ιδίωμα χρησιμοποιείται συνήθως δεν σημαίνει πρέπει χρησιμοποιήστε το. Οι μη ενημερωμένοι προγραμματιστές γράφουν πολλά προγράμματα και απλά η χρήση της Sun Microsystems ή της Microsoft δεν βελτιώνει μαγικά τις δυνατότητες προγραμματισμού ή σχεδιασμού κάποιου. Τα πακέτα Java περιέχουν πολύ μεγάλο κώδικα. Υπάρχουν όμως και τμήματα αυτού του κώδικα, είμαι βέβαιος ότι οι συγγραφείς ντρέπονται να παραδεχτούν ότι έγραψαν.

Με τον ίδιο τρόπο, το μάρκετινγκ ή τα πολιτικά κίνητρα συχνά ωθούν τα ιδιώματα σχεδιασμού. Μερικές φορές, οι προγραμματιστές παίρνουν κακές αποφάσεις, αλλά οι εταιρείες θέλουν να προωθήσουν τι μπορεί να κάνει η τεχνολογία, οπότε υπογραμμίζουν ότι ο τρόπος που το κάνετε είναι λιγότερο από ιδανικός. Κάνουν το καλύτερο από μια κακή κατάσταση. Κατά συνέπεια, ενεργείτε ανεύθυνα όταν υιοθετείτε οποιαδήποτε πρακτική προγραμματισμού απλώς και μόνο επειδή "έτσι πρέπει να κάνετε πράγματα." Πολλά αποτυχημένα έργα Enterprise JavaBeans (EJB) αποδεικνύουν αυτήν την αρχή. Η τεχνολογία που βασίζεται στο EJB είναι εξαιρετική τεχνολογία όταν χρησιμοποιείται κατάλληλα, αλλά μπορεί κυριολεκτικά να καταρρίψει μια εταιρεία εάν χρησιμοποιηθεί ακατάλληλα.

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

Αφαίρεση δεδομένων

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

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

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

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

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

Αυτή η αρχή απόκρυψης εφαρμογής οδηγεί σε μια καλή δοκιμή οξέος της ποιότητας ενός συστήματος OO: Μπορείτε να κάνετε τεράστιες αλλαγές σε έναν ορισμό κλάσης - ακόμη και να πετάξετε το όλο πράγμα και να το αντικαταστήσετε με μια εντελώς διαφορετική εφαρμογή - χωρίς να επηρεαστεί κανένας από τον κώδικα που χρησιμοποιεί αυτό αντικείμενα της τάξης; Αυτό το είδος διαμόρφωσης είναι η κεντρική αρχή του προσανατολισμού του αντικειμένου και καθιστά τη συντήρηση πολύ πιο εύκολη. Χωρίς απόκρυψη εφαρμογής, δεν έχει νόημα να χρησιμοποιήσετε άλλες λειτουργίες OO.

Οι μέθοδοι λήψης και ρύθμισης (γνωστές και ως βοηθητικοί παράγοντες) είναι επικίνδυνες για τον ίδιο λόγο δημόσιο Τα πεδία είναι επικίνδυνα: Παρέχουν εξωτερική πρόσβαση σε λεπτομέρειες εφαρμογής. Τι γίνεται αν πρέπει να αλλάξετε τον τύπο του προσπελάσιμου πεδίου; Πρέπει επίσης να αλλάξετε τον τύπο επιστροφής του προσβάτη. Χρησιμοποιείτε αυτήν την τιμή επιστροφής σε πολλά μέρη, επομένως πρέπει επίσης να αλλάξετε όλο αυτόν τον κωδικό. Θέλω να περιορίσω τα αποτελέσματα μιας αλλαγής σε έναν ορισμό μιας κατηγορίας. Δεν θέλω να κυματιστούν σε ολόκληρο το πρόγραμμα.

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

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

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

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

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

Σχεδιάστε τον εαυτό σας

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

ΕΝΑ getIdentity () Η μέθοδος μπορεί επίσης να λειτουργήσει, φυσικά, υπό την προϋπόθεση ότι επιστρέφει ένα αντικείμενο που εφαρμόζει το Ταυτότητα διεπαφή. Αυτή η διεπαφή πρέπει να περιλαμβάνει ένα ζωγράφισε τον εαυτό σου() (ή δώσε μου-α-JComponent-αυτό-αντιπροσωπεύει-τη δική σας ταυτότητα) μέθοδο. Αν και getIdentity ξεκινά με το "get", δεν είναι προσπελάσιμος επειδή δεν επιστρέφει απλώς ένα πεδίο. Επιστρέφει ένα πολύπλοκο αντικείμενο που έχει λογική συμπεριφορά. Ακόμα και όταν έχω ένα Ταυτότητα αντικείμενο, ακόμα δεν έχω ιδέα πώς μια ταυτότητα εκπροσωπείται εσωτερικά.

Φυσικά, α ζωγράφισε τον εαυτό σου() Η στρατηγική σημαίνει ότι εγώ (έκπληξη!) βάζω τον κωδικό UI στη λογική της επιχείρησης. Σκεφτείτε τι συμβαίνει όταν αλλάξουν οι απαιτήσεις της διεπαφής χρήστη. Ας πούμε ότι θέλω να αντιπροσωπεύσω το χαρακτηριστικό με έναν εντελώς διαφορετικό τρόπο. Σήμερα μια «ταυτότητα» είναι ένα όνομα. αύριο είναι όνομα και αριθμός ταυτότητας. την επόμενη μέρα είναι όνομα, αριθμός ταυτότητας και εικόνα. Περιορίζω το εύρος αυτών των αλλαγών σε ένα μέρος του κώδικα. Αν έχω δώροJComponent-αυτό-αντιπροσωπεύει την τάξη ταυτότητάς σας, τότε έχω απομονώσει τον τρόπο με τον οποίο αντιπροσωπεύονται οι ταυτότητες από το υπόλοιπο σύστημα.

Λάβετε υπόψη ότι δεν έχω βάλει κανένα κωδικό UI στη λογική της επιχείρησης. Έχω γράψει το επίπεδο UI σε όρους AWT (Abstract Window Toolkit) ή Swing, τα οποία είναι και τα δύο επίπεδα αφαίρεσης. Ο πραγματικός κωδικός UI βρίσκεται στην εφαρμογή AWT / Swing. Αυτό είναι το βασικό σημείο ενός επιπέδου αφαίρεσης - για να απομονώσετε τη λογική της επιχείρησής σας από τη μηχανική ενός υποσυστήματος. Μπορώ εύκολα να μεταφέρω σε άλλο γραφικό περιβάλλον χωρίς να αλλάξω τον κώδικα, οπότε το μόνο πρόβλημα είναι λίγο ακαταστασία. Μπορείτε εύκολα να εξαλείψετε αυτήν την ακαταστασία μετακινώντας όλο τον κωδικό UI σε μια εσωτερική κλάση (ή χρησιμοποιώντας το μοτίβο σχεδίασης της πρόσοψης).

JavaBeans

Μπορεί να αντιταχθείτε λέγοντας, "Τι γίνεται όμως με το JavaBeans;" Τι γίνεται με αυτά; Μπορείτε σίγουρα να δημιουργήσετε JavaBeans χωρίς getter και ρυθμιστές. ο BeanCustomizer, BeanInfo, και Περιγραφέας Bean όλες οι τάξεις υπάρχουν για ακριβώς αυτόν τον σκοπό. Οι σχεδιαστές προδιαγραφών JavaBean έριξαν το ιδίωμα λήψης / ρυθμιστή στην εικόνα, επειδή νόμιζαν ότι θα ήταν ένας εύκολος τρόπος να φτιάξετε γρήγορα ένα φασόλι - κάτι που μπορείτε να κάνετε ενώ μαθαίνετε πώς να το κάνετε σωστά. Δυστυχώς, κανείς δεν το έκανε.

Οι αξεσουάρ δημιουργήθηκαν αποκλειστικά ως τρόπος επισήμανσης συγκεκριμένων ιδιοτήτων, έτσι ώστε ένα πρόγραμμα δημιουργίας διεπαφών χρήστη ή ισοδύναμο να μπορεί να τα αναγνωρίσει. Δεν πρέπει να καλέσετε μόνοι σας αυτές τις μεθόδους. Υπάρχουν για ένα αυτοματοποιημένο εργαλείο για χρήση. Αυτό το εργαλείο χρησιμοποιεί τα API ενδοσκόπησης στο Τάξη τάξη για να βρείτε τις μεθόδους και να υπολογίσετε την ύπαρξη ορισμένων ιδιοτήτων από τα ονόματα των μεθόδων. Στην πράξη, αυτό το ιδίωμα που βασίζεται στην ενδοσκόπηση δεν λειτούργησε. Έχει κάνει τον κώδικα πολύ περίπλοκο και διαδικαστικό. Οι προγραμματιστές που δεν καταλαβαίνουν την αφαίρεση δεδομένων καλούν πραγματικά τους βοηθούς πρόσβασης και, κατά συνέπεια, ο κώδικας είναι λιγότερο διατηρήσιμος. Για αυτόν τον λόγο, μια δυνατότητα μεταδεδομένων θα ενσωματωθεί στο Java 1.5 (αναμένεται στα μέσα του 2004). Έτσι αντί:

ιδιωτική ιδιότητα public int getProperty () {επιστροφή ιδιοκτησίας; } public void setProperty (int value} {ιδιότητα = τιμή;} 

Θα μπορείτε να χρησιμοποιήσετε κάτι όπως:

ιδιωτική ιδιοκτησία @property int 

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

Πότε είναι εντάξει ένας αξεσουάρ;

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