ΠΟΛΥΜΟΡΦΙΣΜΟΣ. 4.1 Κληρονομικότητα και Αρχή της Υποκατάστασης

Σχετικά έγγραφα
public void printstatement() { System.out.println("Employee: " + name + " with salary: " + salary);

Οντοκεντρικός Προγραμματισμός ΦΡΟΝΤΙΣΤΗΡΙΟ JAVA

Προγραμματισμός Ι. Κλάσεις και Αντικείμενα. Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Σύνθεση και Κληρονομικότητα

Σύνθεση και Κληρονομικότητα

Διαγράμματα Κλάσεων στη Σχεδίαση

Οντοκεντρικός Προγραμματισμός ΦΡΟΝΤΙΣΤΗΡΙΟ JAVA

Εισαγωγή σε αντικειμενοστραφή concepts. Και λίγη C#

Εισαγωγή στον Αντικειμενοστρέφή Προγραμματισμό Διάλεξη #12

Αντικειμενοστρέφεια. Henri Matisse, Harmony in Red, Κωστής Σαγώνας Νίκος Παπασπύρου

Αντικειµενοστραφής Προγραµµατισµός

2.1 Αντικειµενοστρεφής προγραµµατισµός

Κλάσεις και Αντικείµενα

Αναφορές, είκτες και Αλφαριθμητικά

ΑΠΛΗ ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ

ΟΝΤΟΚΕΝΤΡΙΚΟΣ ΠΡΟΓΡ/ΣΜΟΣ C++

class object Database Database Item Item [sub-typing and polymorphism] MusicCD Video MusicCD Video

Διάλεξη 16-17: Πολυμορφισμός (Polymorphism) Διδάσκων: Παναγιώτης Ανδρέου

Κληρονομικότητα. Παύλος Εφραιμίδης pefraimi <at> ee.duth.gr. Java Κληρονομικότητα 1

Το πρόγραμμα HelloWorld.java. HelloWorld. Κλάσεις και Αντικείμενα (2) Ορισμός μιας Κλάσης (1) Παύλος Εφραιμίδης pefraimi <at> ee.duth.

Εισαγωγή στον Προγ/μό Η/Υ

Η κατασκευή αντικειμένων της κλάσης Student μπορεί να πραγματοποιηθεί είτε στη main είτε σε οποιαδήποτε μέθοδο κλάσης:

Οντοκεντρικός Προγραμματισμός

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

Ερωτήσεις και απαντήσεις στα θέματα του κανονισμού κατάρτισης

ΜΑΘΗΜΑ: Αντικειμενοστρεφής Προγραμματισμός

Παύλος Εφραιµίδης. Java. Κληρονοµικότητα

Προγραμματισμός Υπολογιστών με C++

Ενδεικτική περιγραφή μαθήματος

2 Ορισμός Κλάσεων. Παράδειγμα: Μηχανή για Εισιτήρια. Δομή μιας Κλάσης. Ο Σκελετός της Κλάσης για τη Μηχανή. Ορισμός Πεδίων 4/3/2008

Προγραμματισμός Υπολογιστών με C++

Κλήση Συναρτήσεων ΚΛΗΣΗ ΣΥΝΑΡΤΗΣΕΩΝ. Γεώργιος Παπαϊωάννου ( )

Πληροφορική 2. Γλώσσες Προγραμματισμού

Προγραμματισμός Ι. Εισαγωγή στην C++ Δημήτρης Μιχαήλ. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Προγραμματισμός Υπολογιστών με C++

Εισαγωγή στον Αντικειμενοστρέφή Προγραμματισμό Διάλεξη #13

Βασικά της γλώσσας JAVA

(Απλή) Κληρονομικότητα

Κλάσεις. Κατηγορίες Αντικειµένων. Κλάσεις. Φυσικά Αντικείµενα. Χώρος = Οµάδα Φυσικών Αντικειµένων. Πρόγραµµα = Οµάδα

ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ ΠΟΛΥΜΟΡΦΙΣΜΟΣ

Από τη UML στον Κώδικα. Μέρος Α

Η γλώσσα μοντελοποίησης UML. Βασικές αρχές Τεχνολογίας Λογισμικού, 8η αγγ. έκδοση

Αντικειμενοστραφείς Γλώσσες Προγραμματισμού C++ / ROOT

Εαρινό. Ύλη εργαστηρίου, Ασκήσεις Java

Διαγράμματα UML στην Ανάλυση. Μέρος Β Διαγράμματα Κλάσεων Διαγράμματα Αντικειμένων

Αντικειμενοστρεφής Προγραμματισμός Διάλεξη 2 : ΜΕΤΑΒΛΗΤΕΣ ΤΕΛΕΣΤΕΣ & ΕΚΦΡΑΣΕΙΣ ΕΛΕΓΧΟΣ ΡΟΗΣ

Εισαγωγή στον Προγραμματισμό με C++

Προγραμματισμός Υπολογιστών με C++

Κληρονομικότητα. Κληρονομικότητα (inheritance) είναι ο τρόπος µε τον οποίο μία τάξη μπορεί να κληρονομήσει ιδιότητες και συμπεριφορά από άλλες τάξεις.

Διάγραμμα Κλάσεων. Class Diagram

3 Αλληλεπίδραση Αντικειμένων

Τι χρειάζεται ένας φοιτητής για τη σωστή παρακολούθηση και συμμετοχή στο μαθημα;

Προγραμματισμός ΙI (Θ)

Διδάσκων: Παναγιώτης Ανδρέου

HelloWorld. Παύλος Εφραιμίδης. Java Το πρόγραμμα HelloWorld 1

Η γλώσσα μοντελοποίησης UML. I. Sommerville 2006 Βασικές αρχές Τεχνολογίας Λογισμικού, 8η αγγ. έκδοση Κεφ. 7

ΑΡΦΕ ΑΝΣΙΚΕΙΜΕΝΟΣΡΕΥΟΤ ΠΡΟΓΡΑΜΜΑΣΙΜΟΤ. Ιωάννης Φατζηλυγερούδης Αναπληρωτής Καθηγητής Τμήμα Μηχ/κών Η/Υ και Πληροφορικής Πανεπιστήμιο Πατρών

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

Ανάλυση άσκησης. Employee. SalariedEmployee CommissionEmployee HourlyEmployee. BasePlusCommissionEmployee

Ανάπτυξη και Σχεδίαση Λογισμικού

ΠΛΗΡΟΦΟΡΙΚΗ Ι JAVA Τμήμα θεωρίας με Α.Μ. σε 8 & 9 18/10/07

Δομές δεδομένων (Structures) Εισαγωγή στη C++

Κλάσεις και αντικείμενα #include <iostream.h<

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κληρονομικότητα

Ειδικά Θέματα Προγραμματισμού

Δομή Προγράμματος C++, Χειρισμός Μεταβλητών και Συναρτήσεις Εισόδου - Εξόδου

Αντικειμενοστρεφής Προγραμματισμός

Καλές επιτυχίες παιδιά στα υπόλοιπα μαθήματά σας και καλές γιορτές!!!!

Εντολές εισόδου - εξόδου. Εισαγωγή στη C++

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κληρονομικότητα Downcasting Πολυμορφισμός Late Binding

I (JAVA) Ονοματεπώνυμο: Α. Μ.: Δώστε τις απαντήσεις σας ΕΔΩ: Απαντήσεις στις σελίδες των ερωτήσεων ΔΕΝ θα ληφθούν υπ όψην.

ΚΛΗΡΟΝΟΜΙΚΟΤΗΤΑ ΚΑΙ ΠΟΛΥΜΟΡΦΙΣΜΟΣ

Οντοκεντρικός Προγραμματισμός

Οντοκεντρικός Προγραμματισμός

Κλάσεις. Τροποποιητές, ιασυνδέσεις, Πακέτα. Τροποποιητές ελέγχου προσπέλασης µεταβλητών και µεθόδων

Εισαγωγή στον Αντικειμενοστρεφή Προγραμματισμό Διάλεξη #2

Τελικό τεστ - απαντήσεις

Προγραμματισμός Υπολογιστών με C++

Εισαγωγή ΕΙΣΑΓΩΓΗ. Γεώργιος Παπαϊωάννου ( )

ΥΠΟΛΟΓΙΣΤΕΣ ΙΙ. Τι περιλαμβάνει μια μεταβλητή; ΔΕΙΚΤΕΣ. Διεύθυνση μεταβλητής. Δείκτης

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κληρονομικότητα

Αντικειμενοστρεφής Προγραμματισμός

Κληρονομικότητα. 1 Γενικά. 2 Απλή κληρονομικότητα. 15 Ιανουαρίου 2013

6. Εξαιρέσεις στη γλώσσα Java

Εισαγωγή στον Προγραμματισμό

Κλάσεις στη Java. Παύλος Εφραιμίδης. Java Κλάσεις στη Java 1

ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΗΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ

Κλάσεις στη Java. Στοίβα - Stack. Δήλωση της κλάσης. ΗκλάσηVector της Java. Ηκλάση Stack

I (JAVA) Ονοματεπώνυμο: Α. Μ.: Δώστε τις απαντήσεις σας ΕΔΩ: Απαντήσεις στις σελίδες των ερωτήσεων ΔΕΝ θα ληφθούν υπ όψην.

ΑΤΕΙ ΘΕΣΣΑΛΟΝΙΚΗΣ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ ΠΛΗΡΟΦΟΡΙΚΗΣ Αλγοριθμική και Προγραμματισμός. Παναγιώτης Σφέτσος

Αντικειμενοστρεφής Προγραμματισμός

Γενικά (για τις γραπτές εξετάσεις)

Καλές επιτυχίες παιδιά στα υπόλοιπα μαθήματά σας και καλές γιορτές!!!!

Υπερφόρτωση τελεστών

Προγραμματισμός Υπολογιστών με C++

Στοιχειώδης προγραμματισμός σε C++

Αρχές Τεχνολογίας Λογισμικού Εργαστήριο

Εργαστήριο Java. Διδάσκουσα: Εργαστηριακοί Συνεργάτες:

Οντοκεντρικός Προγραμματισμός

Αντικειμενοστρεφής Προγραμματισμός Ενότητα 6: Φιλικές συναρτήσεις. Επικ. Καθηγητής Συνδουκάς Δημήτριος Τμήμα Διοίκησης Επιχειρήσεων (Γρεβενά)

Transcript:

ΠΟΛΥΜΟΡΦΙΣΜΟΣ Λόγω της θεμελιώδους σημασίας της έννοιας του πολυμορφισμού (polymorphism) στην αντικειμενοστρεφή σχεδίαση, κρίνεται σκόπιμο στο σημείο αυτό του βιβλίου να αναλυθεί εκτενέστερα. Ο πολυμορφισμός αποτελεί το βασικό μηχανισμό στον οποίο στηρίζονται οι Αρχές Σχεδίασης, τα Πρότυπα Σχεδίασης και πολλοί Ευρετικοί Κανόνες, αντικείμενα τα οποία θα συζητηθούν στα επόμενα κεφάλαια. Αξίζει να σημειωθεί ότι πολύ συχνά παρανοείται η σημασία του πολυμορφισμού ή συγχέεται με άλλες έννοιες του αντικειμενοστρεφούς προγραμματισμού. Η υλοποίηση ενός συστήματος που παρουσιάζει πολυμορφική συμπεριφορά σε κάποια σημεία του μπορεί να πραγματοποιηθεί σε οποιαδήποτε αντικειμενοστρεφή γλώσσα προγραμματισμού. Ωστόσο, μεταξύ της C++ και της Java υφίσταται μια σημαντική διαφορά η οποία σχετίζεται με τη δυνατότητα εφαρμογής του πολυμορφισμού επιλεκτικά. 4.1 Κληρονομικότητα και Αρχή της Υποκατάστασης Η κληρονομικότητα παρέχει όπως ήδη αναφέρθηκε τη δυνατότητα εξειδίκευσης μιας υ- πάρχουσας κλάσης. Μια κλάση Β η οποία κληρονομεί (ή επεκτείνει) μια κλάση Α, απορροφά ιδιότητες και συμπεριφορά από την κλάση Α, ενώ μπορεί κάλλιστα να δηλώσει επιπλέον μέλη δεδομένων ή μεθόδους. Η κλάση Α σε μια τέτοια σχέση καλείται βασική (όρος που συνηθίζεται στη C++) ή γονική κλάση, πρόγονος ή υπερκλάση (όρος που προτιμάται στη Java). Η κλάση Β καλείται παράγωγη (C++), κλάση παιδί, απόγονος ή υποκλάση (Java). Καθώς μια παράγωγη κλάση αποτελεί ειδικότερη κατηγορία της βασικής κλάσης, ονομάζουμε μια τέτοια σχέση ως σχέση "είναι" ("is-a" relationship). Για παράδειγμα, αν η κλάση Διοικητικός Υπάλληλος (AdminEmployee) είναι παράγωγη κλάση μιας κλάσης Υπάλληλος (Employee), κάθε αντικείμενο της κλάσης Διοικητικός Υπάλληλος "είναι" και ένας Υπάλληλος. Το διάγραμμα κλάσεων UML παρουσιάζεται στο Σχήμα 4.1 και ο αντίστοιχος κώδικας στη συνέχεια. 87

88 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ Employee -name -salary +printincome() AdminEmployee -ovthours -rate +printincome() Σχήμα 4.1: Η κληρονομικότητα υποδηλώνει μια σχέση "είναι" #include <iostream> #include <string> using namespace std; class Employee public: Employee(string text, double amount); void printincome(); protected: string name; double salary; ; Employee::Employee(string text, double amount) name = text; salary = amount; void Employee::printIncome() cout << "Monthly Income for Employee: " << name << ": " << salary << endl; class AdminEmployee : public Employee public: AdminEmployee(string text, double amount, int hours, double wage); void printincome();

4 Πολυμορφισμός 89 private: int ovthours; double rate; ; AdminEmployee::AdminEmployee(string text, double amount, int hours, double wage) : Employee(text, amount) ovthours = hours; rate = wage; void AdminEmployee::printIncome() cout << "Monthly Income for AdminEmployee:" << name << ": " << salary + ovthours * rate << endl; Στην κλάση Υπάλληλος (Employee) δηλώνεται εκτός από τον κατασκευαστή και μία μέθοδος printincome() η οποία εκτυπώνει στην οθόνη το όνομα και το μηνιαίο εισόδημα ενός υπαλλήλου, που συνίσταται απλά στο μισθό του (salary). Οι ιδιότητες name και salary δηλώνονται με προσδιοριστικό ορατότητας protected ώστε να είναι δυνατή η απευθείας προσπέλασή τους από τις παράγωγες κλάσεις. Η κλάση Διοικητικός Υπάλληλος (AdminEmployee) επαναορίζει ή επικαλύπτει (overrides) τη μέθοδο printincome() ώστε αυτή να έχει διαφορετική υλοποίηση: το συνολικό μηνιαίο εισόδημα συνίσταται στο άθροισμα του μισθού και του γινομένου των ωρών υπερωρίας (ovthours) επί την ωριαία αντιμισθία (rate). Στη συνέχεια παρουσιάζεται η συνάρτηση main η οποία έχει το ρόλο ενός προγράμματος πελάτη που χρησιμοποιεί τις κλάσεις. int main() Employee E1("Fred", 1200); E1.printIncome(); AdminEmployee E2("Bob", 1200, 15, 10); E2.printIncome(); return 0; Το πρόγραμμα έχει την αναμενόμενη συμπεριφορά, δηλαδή η κλήση των μεθόδων printincome() επί των αντίστοιχων αντικειμένων έχει ως αποτέλεσμα την εκτύπωση του αντίστοιχου μηνιαίου εισοδήματος για κάθε κατηγορία υπαλλήλων. Αν χρησιμοποιηθούν δείκτες για την κλήση των συναρτήσεων όπως φαίνεται στο ακόλουθο τμήμα κώδικα, η συμπεριφορά του προγράμματος δεν αλλάζει.

90 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ int main() Employee* pe1 = new Employee("Fred", 1200); pe1->printincome(); AdminEmployee* pe2 = new AdminEmployee("Bob", 1200, 15, 10); pe2->printincome(); return 0; Για παράδειγμα, καθώς ο δείκτης pe2 έχει δηλωθεί ως δείκτης προς αντικείμενα της κλάσης AdminEmployee αντίστοιχα, η κλήση της μεθόδου printincome() μέσω του δείκτη, έχει ως αποτέλεσμα την εκτέλεση της μεθόδου printincome() στην κλάση AdminEmployee. Οι δείκτες και τα αντίστοιχα αντικείμενα απεικονίζονται γραφικά στο Σχήμα 4.2. Μέχρι στιγμής δεν έχει αξιοποιηθεί η δυνατότητα του πολυμορφισμού, απλώς μια παράγωγη κλάση επικαλύπτει μια μέθοδο της βασικής κλάσης. Σχήμα 4.2: Κλήση μεθόδων μέσω δεικτών προς τις αντίστοιχες κλάσεις Επειδή, όπως αναφέρθηκε, κάθε αντικείμενο της κλάσης AdminEmployee αποτελεί ταυτοχρόνως και αντικείμενο της κλάσης Employee, στον δείκτη pe1 που είναι δείκτης προς την βασική κλάση, είναι δυνατόν να ανατεθεί η διεύθυνση ενός αντικειμένου ο- ποιασδήποτε υποκλάσης της Employee (Σχήμα 4.3) Αυτό δεν είναι ιδιαιτερότητα της C++: Σε κάθε αντικειμενοστρεφή γλώσσα προγραμματισμού υπάρχει αυτή η δυνατότητα που αποτελεί εφαρμογή της αρχής της υποκατάστασης. Σε δείκτες (ή αναφορές) προς μια βασική κλάση, είναι δυνατόν να ανατεθούν διευθύνσεις αντικειμένων οποιασδήποτε παράγωγης κλάσης. Τα αντικείμενα των παράγωγων κλάσεων, περιλαμβάνουν όλες τις μεθόδους και ιδιότητες της βασικής κλάσης, και οι οποίες είναι γνωστές σε ένα πρόγραμμα πελάτη το οποίο διαθέτει έναν δείκτη προς τη βασική κλάση. Συνέπεια αυτού είναι ότι το πρόγραμμα πελάτης μπορεί να καλέσει τις μεθόδους των αντικειμένων που αποτελούν στιγμιότυπα των παράγωγων κλάσεων, χωρίς να απαιτείται η τροποποίηση της δήλωσης του δείκτη. Το γεγονός αυτό αποτελεί σημαντική υποστήριξη της δυνατότητας

4 Πολυμορφισμός 91 συντήρησης καθώς το πρόγραμμα πελάτης μπορεί να επεκταθεί (να επικοινωνεί δηλαδή με αντικείμενα νέων κλάσεων) χωρίς να απαιτείται η τροποποίηση του κώδικα. Σχήμα 4.3: Δυνατότητα υποκατάστασης. Σε δείκτες προς τη βασική κλάση είναι δυνατό να ανατεθούν οι διευθύνσεις αντικειμένων παράγωγων κλάσεων 4.2 Πολυμορφισμός Στο προηγούμενο παράδειγμα, αν στο δείκτη pe1 προς τη βασική κλάση ανατεθεί η διεύθυνση του αντικειμένου της παράγωγης κλάσης, όπως φαίνεται στο ακόλουθο τμήμα κώδικα, η κλήση της μεθόδου printincome() μέσω του δείκτη pe1 θα έχει ως αποτέλεσμα την εκτέλεση της μεθόδου printincome() της βασικής κλάσης. Αυτό είναι α- ναμενόμενο, καθώς ο μεταγλωττιστής της C++, κατά τη διάρκεια της μεταγλώττισης γνωρίζει μόνο ότι ο δείκτης pe1 είναι δείκτης προς την βασική κλάση, και κατά συνέπεια διασυνδέει την κλήση με τη μέθοδο της βασικής κλάσης. int main() Employee* pe1 = new Employee("Fred", 1200); pe1->printincome(); AdminEmployee E2("Bob", 1200, 15, 10); pe1 = &E2; //ανάθεση διεύθυνσης αντικειμένου υποκλάσης //σε δείκτη προς τη βασική κλάση pe1->printincome(); //στατική διασύνδεση //καλείται η printincome() της Employee return 0; H διασύνδεση μιας μεθόδου με την αντίστοιχη κλάση κατά τη διάρκεια της μεταγλώττισης ονομάζεται στατική διασύνδεση (static binding). Στατική διασύνδεση λαμβάνει χώρα και κατά την κλήση μιας μεθόδου με χρήση του ονόματος ενός αντικειμένου (π.χ. E2.setHours(15);). Στα πλαίσια της κληρονομικότητας, μια τέτοια συμπεριφορά εί-

92 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ ναι επιθυμητή όταν η μέθοδος της βασικής κλάσης δεν επικαλύπτεται στην υποκλάση ή όταν ρητά απαιτείται η κλήση της μεθόδου της βασικής κλάσης. Σε πολλές ωστόσο περιπτώσεις, παρόλο που η κλήση των μεθόδων μπορεί να πραγματοποιείται μέσω ενός δείκτη προς τη βασική κλάση, αυτό που ζητείται είναι η κλήση των μεθόδων της κλάσης, προς την οποία "δείχνει" κάθε φορά ο δείκτης. Για να γίνει πιο ξεκάθαρη η αναγκαιότητα αυτή, θεωρούμε ένα πρόγραμμα πελάτη που διατηρεί έναν πίνακα δεικτών προς τη βασική κλάση. Σε έναν τέτοιο πίνακα, όπως αναφέρθηκε, είναι απολύτως επιτρεπτό να ανατεθούν διευθύνσεις αντικειμένων των παράγωγων κλάσεων της βασικής κλάσης. Για παράδειγμα, θεωρούμε ότι η συνάρτηση main περιέχει έναν πίνακα δεικτών προς αντικείμενα τύπου Employee. Σε ορισμένες από τις θέσεις του πίνακα ανατίθενται διευθύνσεις αντικειμένων Employee ενώ σε άλλες αντικειμένων τύπου AdminEmployee (Σχήμα 4.4). Σχήμα 4.4: Κλήση μεθόδων μέσω δείκτη προς βασική κλάση Αυτό που είναι πλέον ζητούμενο, είναι κατά την κλήση της μεθόδου printincome() (για παράδειγμα σε ένα βρόχο επαναλήψεων για τη σάρωση του πίνακα), να καλείται σε κάθε περίπτωση η κατάλληλη μέθοδος. Αν δηλαδή, ένα στοιχείο του πίνακα "δείχνει" προς αντικείμενο τύπου AdminEmployee, παρόλο που ο πίνακας είναι δηλωμένος ως πίνακας δεικτών προς Employee, θα πρέπει να καλείται η μέθοδος της κλάσης AdminEmployee. Με άλλα λόγια, στο Σχήμα 4.4, αυτό που είναι επιθυμητό, είναι να αποστέλλεται το ίδιο μήνυμα (printincome) σε κάθε στοιχείο του πίνακα, αλλά η απόκριση να είναι διαφορετική με βάση τον δείκτη που βρίσκεται αποθηκευμένος σε κάθε θέση. Για να γίνει αυτό, θα πρέπει να ελέγχεται κατά τη διάρκεια της εκτέλεσης (at runtime) η κλάση στην οποία ανήκει το αντικείμενο προς το οποίο "δείχνει" κάθε φορά ο δείκτης της βασικής κλάσης. Κατά αυτόν τον τρόπο, είναι δυνατόν να διασυνδέεται το όνομα της μεθόδου με την κατάλληλη κλάση. Η δυνατότητα διασύνδεσης μιας μεθόδου με την αντίστοιχη κλάση κατά τη διάρκεια της εκτέλεσης καλείται δυναμική διασύνδεση (dynamic or late binding) και παρέχει τη δυνατότητα για πολυμορφική συμπεριφορά. Από την ετυμολογία του όρου πολυμορφισμός (πολλές μορφές) προκύπτει ότι η απόκριση στην

4 Πολυμορφισμός 93 κλήση μιας μεθόδου (μέσω δείκτη ή αναφοράς προς μια βασική κλάση), διαφέρει, ανάλογα με το συγκεκριμένο στιγμιότυπο προς το οποίο "δείχνει" ο δείκτης ή η αναφορά. Για να μπορέσει να επιτευχθεί η δυναμική διασύνδεση θα πρέπει η μέθοδος της βασικής κλάσης (η οποία επιθυμούμε να παρουσιάσει πολυμορφική συμπεριφορά) να δηλωθεί ως υπερβατή (virtual). Είναι σύνηθες σφάλμα είναι η παράλειψη της δεσμευμένης λέξης virtual στη δήλωση της μεθόδου της βασικής κλάσης. Αυτό έχει ως αποτέλεσμα κατά την κλήση της μεθόδου μέσω ενός δείκτη προς τη βασική κλάση, να εκτελείται πάντοτε η μέθοδος της βασικής κλάσης, ανεξαρτήτως του αν ο δείκτης "δείχνει" κατά την εκτέλεση σε αντικείμενο της παράγωγης κλάσης. Παρόλο που δεν είναι απαραίτητο να δηλωθεί ως υπερβατή και η μέθοδος στις παράγωγες κλάσεις, είναι καλή πρακτική για λόγους συντηρησιμότητας, να τοποθετείται η δεσμευμένη λέξη virtual σε ολόκληρη την ιεραρχία των κλάσεων. Στο ακόλουθο τμήμα κώδικα επισημαίνεται το σημείο όπου πραγματοποιείται δυναμική διασύνδεση και επιτυγχάνεται πολυμορφική συμπεριφορά. int main() Employee* employees[5]; employees[0] = new Employee("Fred", 1200); employees[1] = new AdminEmployee("Bob", 1200, 15, 10); employees[2] = new AdminEmployee("Nick", 800, 5, 15); employees[3] = new Employee("John", 1500); employees[4] = new AdminEmployee("Peter", 1000, 3, 20); for(int i=0; i<5; i++) //πολυμορφική συμπεριφορά //(δυναμική διασύνδεση) //κατά τη μεταγλώττιση δεν είναι γνωστό //πού δείχνει ο δείκτης employees[i] //ώστε να διασυνδεθεί με την αντίστοιχη //μέθοδο κλάσης employees[i]->printincome(); return 0; Σε ένα διάγραμμα κλάσεων της UML είναι σχετικά εύκολο να εντοπιστούν τα περιθώρια για πολυμορφική συμπεριφορά, καθώς η βασική κλάση προς την οποία "δείχνει" ο δείκτης κάποιας κλάσης-πελάτη, είναι συνήθως αφηρημένη. Μία ή περισσότερες μέθοδοι της βασικής κλάσης είναι δηλωμένες ως αφηρημένες και ως virtual, ενώ η υλοποίηση "αφήνεται" για τις παράγωγες κλάσεις, από τις οποίες θα δημιουργηθούν και αντικείμενα. Η κλάση πελάτης "βλέπει" μόνο τη βασική κλάση και γνωρίζει τις μεθόδους της. Ωστόσο, μπορεί να λειτουργήσει χωρίς καμία τροποποίηση όταν περάσει ως τιμή στο δείκτη ή στην

94 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ αναφορά η διεύθυνση ενός αντικειμένου οποιασδήποτε από τις παράγωγες κλάσεις. Ακόμα και αν η βασική κλάση δεν είναι αφηρημένη, η ύπαρξη ενός δείκτη από μια κλάση πελάτη μόνο προς τη βασική κλάση, υποδηλώνει πολυμορφική συμπεριφορά. Για το παράδειγμα των υπαλλήλων που εξετάστηκε, το αντίστοιχο διάγραμμα κλάσεων παρουσιάζεται στο Σχήμα 4.5. Client Employee -name -salary +printincome() AdminEmployee -ovthours -rate +printincome() Σχήμα 4.5: Πολυμορφική συμπεριφορά της μεθόδου printincome() 4.3 Μην πληρώνετε ό,τι δεν χρησιμοποιείτε Στη Java, όπως και σε κάθε αντικειμενοστρεφή γλώσσα προγραμματισμού, υπάρχει επίσης η δυνατότητα πολυμορφικής συμπεριφοράς. Ωστόσο, η δυναμική διασύνδεση πραγματοποιείται πάντοτε, χωρίς να απαιτείται η ρητή δήλωση με τη χρήση κάποιας δεσμευμένης λέξης. Κατά συνέπεια, όλες οι κλήσεις μεθόδων που δηλώνονται σε μια υπερκλάση και επικαλύπτονται σε υποκλάσεις, και οι οποίες πραγματοποιούνται μέσω μιας αναφοράς που είναι δηλωμένη ως αναφορά προς την υπερκλάση, καταλήγουν σε πολυμορφισμό. Ουσιαστικά, η κατάσταση αυτή ισοδυναμεί με το να δηλώνονται όλες οι μέθοδοι ως υ- περβατές (virtual). Το γεγονός αυτό, ενώ διευκολύνει σημαντικά το έργο του προγραμματιστή, έχει κόστος στην απόδοση: Η δυναμική διασύνδεση επιτυγχάνεται με την προσθήκη ενός πίνακα υπερβατών συναρτήσεων (Vtable) πριν από κάθε κλάση στη μνήμη του υπολογιστή. Παράλληλα, κατά την κλήση virtual μεθόδων πραγματοποιούνται επιπλέον αποαναφορές του δείκτη μέσω του οποίου καλείται η μέθοδος (συνολικά σε τρία επίπεδα), μέχρις ότου εντοπιστεί το κατάλληλο αντικείμενο που περιλαμβάνει την προς εκτέλεση μέθοδο. Η κλήση μιας συνάρτησης μέσω δυναμικής διασύνδεσης απαιτεί για το λόγο αυτό περισσότερο χρόνο (περίπου 1ns ανά πολυμορφική κλήση) και περισσότερη μνήμη από ότι μια κανονική κλήση.

4 Πολυμορφισμός 95 Με βάση τα ανωτέρω, στη C++ επιτυγχάνεται στη γενική περίπτωση βελτίωση της απόδοσης καθώς ο προγραμματιστής "πληρώνει" το κόστος του πολυμορφισμού μόνο όποτε το επιλέγει. Αυτής είναι μια εγγενής σχεδιαστική διαφοροποίηση της Java και επηρεάζει σημαντικά την αποδοτικότητά της.