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

Το Encapsulation δεν κρύβει πληροφορίες

Οι λέξεις είναι ολισθηρές. Όπως ο Humpty Dumpty διακηρύχθηκε στον Lewis Carroll's Μέσα από το γυαλί, "Όταν χρησιμοποιώ μια λέξη, αυτό σημαίνει ακριβώς αυτό που επιλέγω να σημαίνει - ούτε περισσότερο ούτε λιγότερο." Σίγουρα η κοινή χρήση των λέξεων ενθυλάκωση και απόκρυψη πληροφοριών φαίνεται να ακολουθεί αυτή τη λογική. Οι συγγραφείς σπάνια διακρίνουν μεταξύ των δύο και συχνά ισχυρίζονται ότι είναι οι ίδιοι.

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

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

Ωστόσο, η απόκρυψη δεδομένων δεν είναι η πλήρης έκταση της απόκρυψης πληροφοριών. Ο David Parnas εισήγαγε για πρώτη φορά την έννοια της απόκρυψης πληροφοριών γύρω στο 1972. Υποστήριξε ότι τα πρωταρχικά κριτήρια για τη διαμόρφωση του συστήματος πρέπει να αφορούν την απόκρυψη κρίσιμων αποφάσεων σχεδιασμού. Τόνισε ότι κρύβει «δύσκολες σχεδιαστικές αποφάσεις ή αποφάσεις σχεδιασμού που είναι πιθανό να αλλάξουν». Η απόκρυψη πληροφοριών με αυτόν τον τρόπο απομονώνει τους πελάτες από το να απαιτούν οικεία γνώση του σχεδιασμού για να χρησιμοποιήσουν μια ενότητα και από τα αποτελέσματα της αλλαγής αυτών των αποφάσεων.

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

Κατηγορία θέσης

Με την αυξανόμενη συνειδητοποίηση του τεράστιου δυναμικού του ασύρματου Διαδικτύου, πολλοί ειδικοί αναμένουν ότι οι υπηρεσίες βάσει τοποθεσίας θα παρέχουν την ευκαιρία για την πρώτη εφαρμογή ασύρματου killer. Για τον δείγμα κώδικα αυτού του άρθρου, έχω επιλέξει μια τάξη που αντιπροσωπεύει τη γεωγραφική θέση ενός σημείου στην επιφάνεια της γης. Ως οντότητα τομέα, η κλάση, ονομάζεται Θέση, αντιπροσωπεύει πληροφορίες του Global Position System (GPS). Μια πρώτη κοπή στην τάξη φαίνεται τόσο απλή όσο:

Δημόσια τάξη Θέση {δημόσιο διπλό γεωγραφικό πλάτος; δημόσιο διπλό μήκος } 

Η τάξη περιέχει δύο στοιχεία δεδομένων: GPS γεωγραφικό πλάτος και γεωγραφικό μήκος. Στο παρόν, Θέση δεν είναι τίποτα περισσότερο από μια μικρή τσάντα δεδομένων. Παρ 'όλα αυτά, Θέση είναι μια τάξη, και Θέση τα αντικείμενα μπορεί να είναι instantiated χρησιμοποιώντας την κλάση. Για να χρησιμοποιήσετε αυτά τα αντικείμενα, τάξη Θέση περιέχει μεθόδους για τον υπολογισμό της απόστασης και της κατεύθυνσης - δηλαδή της κατεύθυνσης - μεταξύ των καθορισμένων Θέση αντικείμενα:

δημόσια τάξη PositionUtility {δημόσια στατική διπλή απόσταση (Θέση θέσης1, Θέση θέσης2) {// Υπολογίστε και επιστρέψτε την απόσταση μεταξύ των καθορισμένων θέσεων. } δημόσια στατική διπλή επικεφαλίδα (Θέση θέσης1, Θέση θέσης2) {// Υπολογίστε και επιστρέψτε την επικεφαλίδα από τη θέση1 στη θέση2. }} 

Παραλείπω τον πραγματικό κωδικό εφαρμογής για τους υπολογισμούς απόστασης και κατεύθυνσης.

Ο παρακάτω κώδικας αντιπροσωπεύει μια τυπική χρήση του Θέση και Θέση:

// Δημιουργία θέσης που αντιπροσωπεύει το σπίτι μου Θέση myHouse = νέα θέση (); myHouse.latitude = 36.538611; myHouse.longitude = -121.797500; // Δημιουργήστε μια θέση που αντιπροσωπεύει μια τοπική καφετέρια Θέση coffeeShop = νέα θέση (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121.907222; // Χρησιμοποιήστε ένα PositionUtility για να υπολογίσετε την απόσταση και την κατεύθυνση από το σπίτι μου // προς την τοπική καφετέρια. διπλή απόσταση = PositionUtility.distance (myHouse, coffeeShop); διπλή επικεφαλίδα = PositionUtility.heading (myHouse, coffeeShop); // Εκτύπωση αποτελεσμάτων System.out.println ("Από το σπίτι μου στο (" + myHouse.latitude + "," + myHouse.longitude + ") στο καφενείο στο (" + coffeeShop.latitude + "," + coffeeShop. γεωγραφικό μήκος + ") είναι μια απόσταση" + απόσταση + "σε μια επικεφαλίδα" + επικεφαλίδα + "μοίρες."); 

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

 ================================================== ================= Από το σπίτι μου στο (36.538611, -121.7975) έως το καφενείο στο (36.539722, -121.907222) είναι απόσταση 6.0873776351893385 με τίτλο 270.7547022304523 βαθμούς. ================================================== ================= 

Θέση, Θέση, και η χρήση του κώδικα είναι λίγο ανησυχητική και σίγουρα δεν είναι πολύ αντικειμενική. Αλλά πώς μπορεί να είναι αυτό; Η Java είναι μια αντικειμενοστρεφής γλώσσα και ο κώδικας χρησιμοποιεί αντικείμενα!

Αν και ο κώδικας μπορεί να χρησιμοποιεί αντικείμενα Java, το κάνει με τρόπο που θυμίζει μια παλιή εποχή: λειτουργίες χρησιμότητας που λειτουργούν σε δομές δεδομένων. Καλώς ήλθατε στο 1972! Καθώς ο Πρόεδρος Νίξον μίλησε για μυστικές μαγνητοσκοπήσεις, οι επαγγελματίες υπολογιστών που κωδικοποιούσαν στη διαδικαστική γλώσσα η Fortran χρησιμοποίησε με ενθουσιασμό τη νέα Διεθνή Βιβλιοθήκη Μαθηματικών και Στατιστικών (IMSL) με αυτόν τον τρόπο. Τα αποθετήρια κώδικα όπως το IMSL ήταν γεμάτα με συναρτήσεις για αριθμητικούς υπολογισμούς. Οι χρήστες πέρασαν δεδομένα σε αυτές τις λειτουργίες σε λίστες παραμέτρων μεγάλων παραμέτρων, οι οποίες κατά καιρούς περιελάμβαναν όχι μόνο την είσοδο αλλά και τις δομές δεδομένων εξόδου. (Το IMSL εξακολούθησε να εξελίσσεται με την πάροδο των ετών και μια έκδοση είναι πλέον διαθέσιμη για προγραμματιστές Java.)

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

Ομαδοποίηση δεδομένων και μεθόδων

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

δημόσια τάξη Θέση {δημόσια διπλή απόσταση (Θέση θέσης) {// Υπολογίστε και επιστρέψτε την απόσταση από αυτό το αντικείμενο στην καθορισμένη // θέση. } δημόσια διπλή επικεφαλίδα (Θέση θέσης) {// Υπολογίστε και επιστρέψτε την επικεφαλίδα από αυτό το αντικείμενο στην καθορισμένη // θέση. } δημόσιο διπλό γεωγραφικό πλάτος; δημόσιο διπλό μήκος } 

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

Θέση myHouse = νέα θέση (); myHouse.latitude = 36.538611; myHouse.longitude = -121.797500; Θέση coffeeShop = νέα θέση (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121.907222; διπλή απόσταση = myHouse.distance (coffeeShop); διπλή επικεφαλίδα = myHouse.heading (coffeeShop); System.out.println ("Από το σπίτι μου στο (" + myHouse.latitude + "," + myHouse.longitude + ") στο καφενείο στο (" + coffeeShop.latitude + "," + coffeeShop.longitude + ") είναι μια απόσταση "+ απόσταση +" σε μια επικεφαλίδα "+ επικεφαλίδα +" μοίρες. "); 

Η έξοδος είναι πανομοιότυπη με πριν, και το πιο σημαντικό, ο παραπάνω κώδικας φαίνεται πιο φυσικός. Η προηγούμενη έκδοση πέρασε δύο Θέση αντικείμενα σε μια συνάρτηση σε μια ξεχωριστή κλάση χρησιμότητας για τον υπολογισμό της απόστασης και της κατεύθυνσης Σε αυτόν τον κωδικό, τον υπολογισμό της επικεφαλίδας με τη μέθοδο κλήσης util.heading (myHouse, καφενείο) δεν έδειξε σαφώς την κατεύθυνση του υπολογισμού. Ένας προγραμματιστής πρέπει να θυμάται ότι η συνάρτηση βοηθείας υπολογίζει την επικεφαλίδα από την πρώτη παράμετρο στη δεύτερη.

Σε σύγκριση, ο παραπάνω κώδικας χρησιμοποιεί τη δήλωση myHouse.heading (καφενείο) για τον υπολογισμό της ίδιας επικεφαλίδας. Η σημασιολογία της κλήσης δείχνει ξεκάθαρα ότι η κατεύθυνση προχωρά από το σπίτι μου στο καφενείο. Μετατροπή της συνάρτησης δύο ορισμάτων τίτλος (Θέση, Θέση) σε μια συνάρτηση με ένα όρισμα position.heading (Θέση) είναι γνωστό ως κάρι η λειτουργία. Το Currying εξειδικεύει αποτελεσματικά τη λειτουργία στο πρώτο επιχείρημά της, με αποτέλεσμα πιο σαφή σημασιολογία.

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

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

Αμυντικός προγραμματισμός

Για να διερευνήσουμε περαιτέρω τις συνέπειες της έκθεσης στοιχείων εσωτερικών δεδομένων, ας υποθέσουμε ότι αποφασίζω να προσθέσω λίγο αμυντικό προγραμματισμό Θέση περιορίζοντας το γεωγραφικό πλάτος και μήκος σε περιοχές που καθορίζονται από το GPS. Το γεωγραφικό πλάτος πέφτει στο εύρος [-90, 90] και το μήκος στο εύρος (-180, 180). Η έκθεση των στοιχείων δεδομένων γεωγραφικό πλάτος και γεωγραφικό μήκος σε ΘέσηΗ τρέχουσα εφαρμογή καθιστά αυτόν τον αμυντικό προγραμματισμό αδύνατο.

Δημιουργία χαρακτηριστικών γεωγραφικού πλάτους και μήκους ιδιωτικός μέλη δεδομένων της τάξης Θέση και η προσθήκη απλών μεθόδων προσπέλασης και μεταλλάκτη, που συνήθως ονομάζονται getter and setter, παρέχει μια απλή λύση για την έκθεση πρώτων στοιχείων δεδομένων. Στο παρακάτω παράδειγμα κώδικα, οι μέθοδοι ρύθμισης ελέγχουν κατάλληλα τις εσωτερικές τιμές του γεωγραφικό πλάτος και γεωγραφικό μήκος. Αντί να ρίξω μια εξαίρεση, καθορίζω την εκτέλεση modo αριθμητικής στις τιμές εισόδου για να διατηρήσω τις εσωτερικές τιμές εντός καθορισμένων ορίων. Για παράδειγμα, η απόπειρα ρύθμισης του γεωγραφικού πλάτους σε 181.0 οδηγεί σε εσωτερική ρύθμιση -179,0 για γεωγραφικό πλάτος.

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

δημόσια τάξη Θέση {δημόσια θέση (διπλό γεωγραφικό πλάτος, διπλό μήκος) {setLatitude (γεωγραφικό πλάτος); setLongitude (γεωγραφικό μήκος); } public void setLatitude (διπλό γεωγραφικό πλάτος) {// Βεβαιωθείτε -90 <= γεωγραφικό πλάτος <= 90 χρησιμοποιώντας αριθμητική modulo. // Ο κωδικός δεν εμφανίζεται. // Στη συνέχεια, ορίστε μεταβλητή παρουσίας. this.latitude = γεωγραφικό πλάτος; } public void setLongitude (διπλό μήκος) {// Βεβαιωθείτε ότι -180 <γεωγραφικό μήκος <= 180 χρησιμοποιώντας αριθμητική μονάδα. // Ο κωδικός δεν εμφανίζεται. // Στη συνέχεια, ορίστε μεταβλητή παρουσίας. this.longitude = γεωγραφικό μήκος; } δημόσιο διπλό getLatitude () {επιστροφή γεωγραφικού πλάτους; } δημόσιο διπλό getLongitude () {επιστροφή γεωγραφικού μήκους; } δημόσια διπλή απόσταση (Θέση θέσης) {// Υπολογίστε και επιστρέψτε την απόσταση από αυτό το αντικείμενο στην καθορισμένη θέση //. // Ο κωδικός δεν εμφανίζεται. } δημόσια διπλή επικεφαλίδα (Θέση θέσης) {// Υπολογίστε και επιστρέψτε την επικεφαλίδα από αυτό το αντικείμενο στην καθορισμένη // θέση. } ιδιωτικό διπλό γεωγραφικό πλάτος; ιδιωτικό διπλό μήκος } 

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

Θέση myHouse = νέα θέση (36.538611, -121.797500); Θέση coffeeShop = νέα θέση (36.539722, -121.907222); διπλή απόσταση = myHouse.distance (coffeeShop); διπλή επικεφαλίδα = myHouse.heading (coffeeShop); System.out.println ("Από το σπίτι μου στο (" + myHouse.getLatitude () + "," + myHouse.getLongitude () + ") στο καφενείο στο (" + coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () + ") είναι μια απόσταση" + απόσταση + "σε μια επικεφαλίδα" + επικεφαλίδα + "μοίρες."); 

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

Απομόνωση πιθανής αλλαγής

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

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

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