Κυριακή 25 Ιουλίου 2021

Προβλήματα κατονόησης στο Προγραμματισμό με Αντικείμενα!

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

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

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

Ας σκεφτούμε το τι είναι η προσομοίωση. Όταν κάτι "εκτελεί" ίδιες ενέργειες με κάτι άλλο, αλλά σε καμία περίπτωση δεν είναι ταυτόσιμο τότε λέμε ότι το προσομοιώνει; Αν ναι τότε με αυτήν την έννοια κάθε αυτοκίνητο προσομοιώνει το αρχικό αυτοκίνητο ως προς την κίνηση, καθώς και τη μεταφορά προσώπων και πραγμάτων. Ποτέ όμως δεν θα πούμε ότι το νέο μοντέλο αυτοκινήτου Χ είναι προσωμοίωση του προηγούμενου ή του πρώτου αυτοκινήτου. Γιατί το νέο μοντέλο είναι και αυτό ένα αυτοκίνητο, και έχει τα γνωρίσματα των αυτοκινήτων. Έτσι η προσομοίωση πρέπει να έχει κάτι διαφορετικό και εδώ είναι το "ανάλογο" όπως λέμε. Το τεχνικό δόμημα που εκτελεί τις ενέργειες ενός αυτοκινήτου, ο προσομοιωτής, είναι το ανάλογο του αυτοκινήτου χωρίς να είναι αυτοκίνητο.

Δείτε που υπάρχει το σφάλμα. Ένα πρόγραμμα είναι "από τη μάνα του" μια προσομοίωση, ένα τεχνικό δόμημα, που εκτελεί τις ενέργειες ενός αλγόριθμου (ή μιας ομάδας αλγορίθμων). Έτσι τι το διαφορετικό ως προς την προσομοίωση έχει το πρόγραμμα από ένα αντικείμενο; Μήπως είναι το ζήτημα του  αντικείμενου του "πραγματικού κόσμου"; Κανένα πρόγραμμα δεν έχει αποτέλεσμα αν δεν έχει συνάφεια με τον πραγματικό κόσμο. Ένα πρόγραμμα συγγραφής κειμένου κάνει τον υπολογιστή μια γραφομηχανή με εξαιρετικές δυνατότητες, ακόμα και της αρχειοθέτησης. Η δουλειά που γίνεται με το πρόγραμμα συγγραφής κειμένου, δεν εφευρέθηκε με το πρόγραμμα αλλά προυπήρχε ως μια δουλειά πραγματικού κόσμου. Σίγουρα βελτιώθηκε, αφού δεν γίνεται η συγγραφή άμεσα στο χαρτί. Οπότε από το παραπάνω συλλογισμό το μόνο που μένει να δούμε αν πράγματι έχει σχέση με τα αντικείμενα είναι η "δομή".

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

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

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

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

Ας δούμε πάλι τον ορισμό:

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

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

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

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

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

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

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

Η ιεραρχία αντικειμένων (βάσει κληρονομικότητας) ήταν ένα πρόσθετο στον προγραμματισμό με αντικείμενα. Αυτό το πρόσθετο επειδή μπήκε τελευταία στο "πακέτο" έχει γίνει αντικείμενο έκθεσης για τον προγραμματισμό αντικειμένων. Έτσι πάμε σε παραδείγματα όπου το αντικείμενο Σκύλος κληρονόμησε από το αντικείμενο Τετράποδο το οποίο κληρονόμησε από το αντικείμενο Κατοικίδιο. Έτσι μπορώ να έχω και το αντικείμενο Χελώνα το οποίο κληρονόμησε από το αντικείμενο Κατοικίδιο. Κοινές ιδιότητες και μεθόδους βάζουμε στο αντικείμενο Κατοικίδιο που περιέχεται σε ένα Σκύλο και σε μια Χελώνα. Οπότε μπορώ να έχω μια μέθοδο που να παίρνει έναν πίνακα με Κατοικίδια, και εκεί να έχω σε μια θέση ένα Σκύλο και σε μια άλλη θέση μια Χελώνα. Τελικά αν θέλω να προσθέσω κάτι άλλο, όπως ένα Παπαγάλο, αρκεί να κληρονομήσει από το Κατοικίδιο για να μπορεί να βρίσκεται στο πίνακα τύπου Κατοικίδιο άρα χωρίς να πειράξω το υποπρόγραμμα που τον χρησιμοποιεί. Αυτή είναι η ουσία τεχνικά της Ιεραρχίας στα αντικείμενα, η οποία αναφέρεται ως ιεραρχία κλάσεων ή προτύπων ανάλογα με το πως φτιάχνονται τα αντικείμενα. Στο παράδειγμα όλα τα κατοικίδια έχουν όνομα, οπότε μια μέθοδος θέσε_όνομα θα υπάρχει στο αντικείμενο Κατοικίδιο.

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

Η ιδέα της Ιεραρχίας Κλάσεων ως κληρονομικότητα αντικειμένων, δημιουργεί την εντύπωση ότι τα αντικείμενα έχουν δυο όψεις: ο ορισμός και η υλοποίηση στη μνήμη. Ο ορισμός, ή το πατρόν, είναι η Κλάση. Σε κάποιες γλώσσες ισχύει, σε κάποιες άλλες όχι, γιατί ο ορισμός μπορεί να φτιάχνει άμεσα το αντικείμενο, και το κάθε νέο να είναι αντίγραφο! Ιεραρχίες αντικειμένων μπορούμε να έχουμε και με τα πρότυπα! Έτσι η λέξη Κλάση απλά μπερδεύει κάποιον που δεν γνωρίζει τη διαφορά μηχανισμού Κλάσης/Πρότυπου όταν αργότερα θα πάει σε μια γλώσσα που δεν έχει κλάσεις (ή οι κλάσεις που έχει είναι στην ουσία συναρτήσεις που επιστρέφουν Αντικείμενα που μπορούν να χρησιμοποιηθούν ως Πρότυπα).

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

ΓΚ

Υ.Γ.

Καλές Καλοκαιρινές διακοπές!