Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής Αναγκαιότητα Κλάσεων

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

public void printstatement() { System.out.println("Employee: " + name + " with salary: " + salary);

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Graphical User Interfaces (GUI) SWING

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

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

ημιουργία Γραφικού Περιβάλλοντος

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

Week 10: Graphical User Interfaces

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

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Σύνθεση αντικειμένων Παράδειγμα: Τμήμα πανεπιστημίου

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πίνακες Κλάσεις και Αντικείμενα

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Δημιουργώντας δικές μας Κλάσεις και Αντικείμενα

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Μέθοδοι

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

ΠΛΗΡΟΦΟΡΙΚΗ ΙΙ (JAVA) 11/3/2008

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Σύνθεση αντικειμένων

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πολυμορφισμός Αφηρημένες κλάσεις Interfaces (διεπαφές)

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Σύνθεση αντικειμένων

Ανάλυση Πληροφοριακών Συστημάτων. Εαρινό Εξάμηνο Lec08 09/04/2019 Διδάσκων: Γεώργιος Χρ. Μακρής

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πολυμορφισμός Αφηρημένες κλάσεις Interfaces (διεπαφές)

Εργαστήριο Τεχνολογίας Λογισμικού και Ανάλυσης Συστημάτων - 4 ο Εργαστήριο -

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

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

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

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

Περιεχόµενα. 1 Εισαγωγή στις οµές εδοµένων 3. 2 Στοίβα (Stack) 5

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Graphical User Interfaces (GUI) SWING

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Μαθήματα από τα εργαστήρια

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

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

Εργαστήριο 1-1 η Άσκηση - Ανάλυση

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

Μοντελοποίηση Συστημάτων. Διαγράμματα Κλάσεων ClassDiagrams

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Δημιουργία Κλάσεων και Αντικειμένων

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

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

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

Abstract classes, Interfaces ΦΡΟΝΤΙΣΤΗΡΙΟ JAVA

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Μέθοδοι

Εργαστήριο 2 - Άσκηση - Ανάλυση

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

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

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

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

Εισαγωγή στον Προγραµµατισµό, Αντώνιος Συµβώνης, ΣΕΜΦΕ, ΕΜΠ,, Slide 6

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Μέθοδοι

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Μέθοδοι

ΠΛΗΡΟΦΟΡΙΚΗ ΙI Ενότητα 3: Έλεγχος ροής προγράμματος

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Δημιουργία Κλάσεων και Αντικειμένων Constructors

Εφαρμογή Μεθοδολογίας ICONIX

Wrapper Classes, Abstract Classes and Interfaces

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

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

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Αναφορές

ΠΛΗΡΟΦΟΡΙΚΗ ΙΙ (JAVA) 18/3/2008

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Εισαγωγή στη Java

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πίνακες Κλάσεις και Αντικείμενα

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

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

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

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Υπάρχουσες κλάσεις και αντικείμενα στην Java Strings Wrapper Classes Δομές

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

Εργαστήριο 02: Προγραμματισμός με Γενικούς Τύπους (JAVA Generics) ΕΠΛ231 Δομές Δεδομένων και Αλγόριθμοι

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αναφορές Στοίβα και Σωρός μνήμης Αντικείμενα παράμετροι String Interning

J-GANNO. Σύντοµη αναφορά στους κύριους στόχους σχεδίασης και τα βασικά χαρακτηριστικά του πακέτου (προέκδοση 0.9Β, Φεβ.1998) Χάρης Γεωργίου

Περιεχόμενα. Πρόλογος 15

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Πολυμορφισμός Αφηρημένες κλάσεις Interfaces (διεπαφές)

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

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

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

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

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

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

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

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Κλάσεις και Αντικείμενα Constructors

Αντικειμενοστραφής Προγραμματισμός I (5 ο εξ) Εργαστήριο #2 ο : Ανατομία προγραμμάτων εφαρμογών, η

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

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα με πίνακες. Constructors. Υλοποίηση Στοίβας

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

Transcript:

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής Αναγκαιότητα Κλάσεων Θεωρούμε το κάτωθι απλό πρόγραμμα, το οποίο, αν και γραμμένο στη γλώσσα προγραμματισμού Java δεν έχει καθόλου αντικειμενοστρεφή χαρακτηριστικά. Όλος ο κώδικας βρίσκεται μέσα στη μέθοδο main (στατική μέθοδος που πρέπει να υφίσταται σε κάθε πρόγραμμα Java δεδομένου ότι από αυτήν ξεκινά πάντοτε η εκτέλεση του προγράμματος). Το πρόγραμμα λαμβάνει για ένα πλήθος υποθετικών προϊόντων (για όσα θέλει να καταχωρήσει ο χρήστης), το όνομά τους, την τιμή τους και το σκορ τους, βάσει κάποιας κλίμακας αξιολόγησης. Μετά το πέρας της καταχώρησης των στοιχείων το πρόγραμμα εκτυπώνει το "βέλτιστο" προϊόν, δηλαδή αυτό που επιτυγχάνει τον καλύτερο λόγο σκορ/τιμή. Από πλευράς μεταβλητών, χρησιμοποιεί 3 μεταβλητές για την καταχώρηση του τρέχοντος προϊόντος σε κάθε επανάληψη (next_name, next_price, next_score) και 3 μεταβλητές για την αποθήκευση των στοιχείων του βέλτιστου προϊόντος σε κάθε επανάληψη (best_name, best_price, best_score). Πρώτη εκδοχή προγράμματος (μη αντικειμενοστρεφής) import java.util.scanner; public class Main { /** * @param args */ public static void main(string[] args) { Scanner in = new Scanner(System.in); String best_name = ""; double best_price = 1; int best_score = 0; boolean more = true; while(more) { String next_name; double next_price; int next_score; System.out.println("Please enter the product name: "); next_name = in.nextline(); System.out.println("Please enter the product price: "); next_price = in.nextdouble(); System.out.println("Please enter the product score: "); next_score = in.nextint(); if(next_score/next_price > best_score/best_price) { best_name = next_name;

Αλέξανδρος Χατζηγεωργίου, 2012 best_price = next_price; best_score = next_score; System.out.println("More data? (1=YES, 2=NO)"); int answer = in.nextint(); if(answer!= 1) more = false; in.nextline(); System.out.println("The best product is: " + best_name); System.out.println("The best price is: " + best_price); System.out.println("The best score is: " + best_score); Αν εξετάσουμε το πρόγραμμα προσεκτικά, και σε ένα πιο αφαιρετικό επίπεδο, θα διαπιστώσουμε ότι στο πρόγραμμα υποκρύπτονται δύο "έννοιες", αυτή του τρέχοντος προϊόντος και του βέλτιστου προϊόντος. Από πλευράς χαρακτηριστικών, και το τρέχον και το βέλτιστο προϊόν έχουν τις ίδιες ιδιότητες (όνομα, τιμή και σκορ), κατά περίπτωση όμως διαφορετικές τιμές σε αυτές τις ιδιότητες (σε κάποιο δεδομένο σημείο της εκτέλεσης το βέλτιστο μπορεί να είναι διαφορετικό από το τρέχον προϊόν που καταχωρήθηκε). Επομένως, στο συγκεκριμένο πρόγραμμα, υπονοείται η ύπαρξη μιας "κατηγορίας" αντικειμένων τύπου "Προϊόν", έννοια που στις αντικειμενοστρεφείς γλώσσες προγραμματισμού μοντελοποιείται από μια κλάση. Μια κλάση αποτελεί την γενική κατηγορία, την "μήτρα" από την οποία μπορούν να παραχθούν διάφορα στιγμιότυπα αντικείμενα. Όλα τα αντικείμενα μιας κλάσης έχουν τις ίδιες ιδιότητες (και την ίδια συμπεριφορά), λαμβάνουν όμως διαφορετικές τιμές για τις ιδιότητες αυτές. Στην προκειμένη περίπτωση υπονοείται η ύπαρξη της κλάσης "Προϊόν" με ιδιότητες το όνομα, σκορ και τιμή, ενώ το τρέχον και το βέλτιστο προϊόν αποτελούν στιγμιότυπα της κλάσης αυτής. Για την εύρεση των λειτουργιών/μεθόδων που πρέπει να περιλαμβάνει η κλάση "Προϊόν" διερευνούμε τον υπάρχοντα κώδικα (1η εκδοχή) για να δούμε τις λειτουργίες στις οποίες εμπλέκεται ένα αντικείμενο τύπου "Προϊόν". Προκύπτει ότι: - ένα προϊόν πρέπει να μπορεί να αρχικοποιήσει τις τιμές των ιδιοτήτων του. Αυτό επιτυγχάνεται με την προσθήκη ενός κατασκευαστή στην κλάση "Προϊόν". Ο κατασκευαστής αποτελεί ειδική μέθοδο μιας κλάσης που καλείται οποτεδήποτε δημιουργούμε ένα αντικείμενο της κλάσης. Στους κατασκευαστές συνήθως συμπεριλαμβάνουμε και την αρχικοποίηση των τιμών των ιδιοτήτων που επιθυμούμε. - για ένα προϊον θα πρέπει να μπορούμε να "διαβάσουμε" τιμές από το πληκτρολόγιο και να τις καταχωρήσουμε στις αντίστοιχες ιδιότητές του. Επομένως "εφοδιάζουμε" την κλάση "Προϊόν" και με μια μέθοδο read(). - για κάθε προϊον, θα πρέπει να μπορούμε να συγκρίνουμε τον εν λόγω προϊόν με κάποιο άλλο προϊόν για να βρούμε ποιο από τα 2 είναι το καλύτερο (με βάση το λόγο σκορ/τιμή). Επομένως

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής "εφοδιάζουμε" την κλάση "Προϊόν" και με μια μέθοδο is_better_than(product other). Η μέθοδος καλείται επί ενός αντικειμένου (έστω X) μεταβιβάζοντας ως παράμετρο ένα άλλο αντικείμενο τύπου Προϊόν (έστω other). Η μέθοδος επιστρέφει true αν το X είναι καλύτερο από το other. - τέλος, για ένα προϊον θα πρέπει να μπορούμε να εκτυπώσουμε τις τιμές των ιδιοτήτων τους. Επομένως "εφοδιάζουμε" την κλάση "Προϊόν" και με μια μέθοδο printdata(). Με βάση τα ανωτέρω, ο ορισμός της κλάσης Product είναι ως εξής: Δεύτερη εκδοχή προγράμματος (αντικειμενοστρεφής) import java.util.scanner; public class Product { private String name; private int score; private double price; //κατασκευαστής public Product() { name = ""; score = 0; price = 1; //μέθοδος ανάγνωσης τιμών από το πληκτρολόγιο public void read() { Scanner in = new Scanner(System.in); System.out.println("Enter product name: "); name = in.nextline(); System.out.println("Enter product score: "); score = in.nextint(); System.out.println("Enter product price: "); price = in.nextdouble(); //μέθοδος σύγκρισης προϊόντος με ένα άλλο public boolean is_better_than(product other) { if(score/price > other.score/other.price) return true; return false; //μέθοδος εκτύπωσης τιμών ιδιοτήτων public void printdata() { System.out.println("Product Name: " + name); System.out.println("Price: " + price); System.out.println("Score: " + score);

Αλέξανδρος Χατζηγεωργίου, 2012 Έχοντας πλέον την κλάση Προϊόν στη διάθεσή μας, μπορούμε να την αξιοποιήσουμε κατασκευάζοντας αντικείμενα της κλάσης στη συνάρτηση main και καλώντας τις κατάλληλες μεθόδους: import java.util.scanner; public class Main { public static void main(string[] args) { Scanner in = new Scanner(System.in); //κατασκευή βέλτιστου προϊόντος Product best = new Product(); boolean flag = true; while(flag) { //κατασκευή τρέχοντος προϊόντος Product current = new Product(); current.read(); //αποστολή μηνύματος read στο αντικείμενο current //κλήση της μεθόδου read επί του αντικειμένου current if(current.is_better_than(best)) best = current; System.out.println("CONTINUE? (1=YES, 2=NO)"); int answer = in.nextint(); if(answer!= 1) flag = false; in.nextline(); //flushes the input buffer System.out.println("Best Product: "); best.printdata(); Στη δεύτερη εκδοχή του προγράμματος, αν και με στοιχειώδη τρόπο, η συμπεριφορά είναι αντικειμενοστρεφής, δηλαδή η λειτουργικότητα επιτυγχάνεται μέσω της αλληλεπίδρασης των αντικειμένων που δημιουργούνται (η αλληλεπίδραση συνίσταται στην σύγκριση των δύο αντικειμένων στην προκειμένη περίπτωση).

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής Δημιουργία Κλάσεων/Αντικειμένων/Συσχετίσεων/Συνδέσεων Δημιουργία κλάσεων και αντικειμένων Θεωρούμε ένα υποθετικό σύστημα που αναφέρεται σε Φοιτητές και Μαθήματα. Για τη μοντελοποίηση των Φοιτητών δημιουργούμε την κλάση Student. Η κλάση διαθέτει ως ιδιότητες το όνομα και τον αριθμό μητρώου του φοιτητή, ενώ από πλευράς μεθόδων πέραν των κατασκευαστών και των μεθόδων πρόσβασης διαθέτει και μια μέθοδο εκτύπωσης των τιμών των ιδιοτήτων. public class Student { private String name; private String id; public Student(String text1, String text2) { name = text1; id = text2; public Student(String text1) { name = text1; id = "9999"; public String getname() { return name; public String getid() { return id; public void printinfo() { System.out.println("Name: " + name); System.out.println("AM: " + id); Η κατασκευή αντικειμένων της κλάσης Student μπορεί να πραγματοποιηθεί είτε στη main είτε σε οποιαδήποτε μέθοδο κλάσης: public class Main { public static void main(string[] args) { Student S1 = new Student("John", "1209"); Student S2 = new Student("Nick"); S1.printInfo();

Αλέξανδρος Χατζηγεωργίου, 2012 S2.printInfo(); Το τμήμα της εντολής Student S1 = new Student("John", "1209"); μετά τον τελεστή "=", δηλαδή: new Student("John", "1209"); δημιουργεί ένα αντικείμενο της κλάσης Student καλώντας τον αντίστοιχο κατασκευαστή και μεταβιβάζοντας ως ορίσματα το όνομα και τον ΑΜ του φοιτητή. Το τμήμα της εντολής πριν από τον τελεστή "=", δηλαδή Student S1 δημιουργεί μια αναφορά τύπου Student, δηλαδή μια μεταβλητή της οποίας το περιεχόμενο μπορεί να λάβει ως τιμή τις διευθύνσεις αντικειμένων τύπου Student (ουσιαστικά πρόκειται για έναν "δείκτη"). Μετά την εκτέλεση της εντολής, ανατίθεται στην αναφορά S1 η διεύθυνση του αντικειμένου που δημιουργήθηκε και κατά συνέπεια η αναφορά "δείχνει" πλέον στο δημιουργηθέν αντικείμενο. Αυτό μπορεί να παρασταθεί διαγραμματικά ως εξής: S1 John 1209 Επειδή όλες οι ενέργειες επί του αντικειμένου που δημιουργήθηκε πραγματοποιούνται μέσω της αναφοράς S1, καταχρηστικά αναφερόμαστα συχνά στο ίδιο το αντικείμενο ως 'S1'. Ωστόσο θα πρέπει να γίνει κατανοητή η διαφορά μεταξύ μιας αναφοράς ενός αντικειμένου (η οποία προτού αρχικοποιηθεί μπορεί να μην "δείχνει" πουθενά) και του ίδιου του αντικειμένου. Η αποστολή ενός μηνύματος στο αντικείμενο ισοδυναμεί με την κλήση μιας μεθόδου του αντικειμένου. Για παράδειγμα, η αποστολή του μηνύματος printinfo() στο αντικείμενο προς το οποίο δείχνει η αναφορά S1 με σκοπό να εκτυπωθούν στην κονσόλα οι τιμές των ιδιοτήτων του πραγματοποιείται με την εντολή: S1.printInfo(); Δημιουργία συσχετίσεων μεταξύ κλάσεων Για τη μοντελοποίηση ενός μαθήματος δημιουργούμε την κλάση Course: public class Course {

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής private String name; private int credits; public Course(String text, int number) { name = text; credits = number; public String getname() { return name; public int getcredits() { return credits; Για να μοντελοποιήσουμε το γεγονός ότι ένας φοιτητής παρακολουθεί ένα μάθημα (και άρα σχετίζεται με αυτό) δημιουργούμε μια συσχέτιση μεταξύ των κλάσεων Student και Course με κατεύθυνση από την πρώτη προς τη δεύτερη (μονόδρομη συσχέτιση). Μια τέτοια συσχέτιση σε ένα διάγραμμα κλάσεων απεικονίζεται ως εξής: - name - id Student - name - credits Course Η συσχέτιση υποδηλώνει ότι ένα αντικείμενο τύπου Student "γνωρίζει" το αντικείμενο Course με το οποίο σχετίζεται και επιτρέπει την αποστολή μηνυμάτων από το αντικείμενο Student προς το αντικείμενο Course (όχι όμως και το ανάποδο, δεδομένου ότι η συσχέτιση έχει μονόδρομη κατεύθυνση). Η συσχέτιση υλοποιείται στην κλάση Student ως μια ιδιότητα που είναι αναφορά τύπου Course: private Course mycourse; προφανώς, αν η αναφορά αυτή δεν λάβει συγκεκριμένη τιμή δεν "δείχνει" πουθενά. Μπορούμε να αναθέσουμε τιμή σε μια αναφορά όπως και σε οποιαδήποτε άλλη ιδιότητα. Αν υποθέσουμε ότι η αναφορά λαμβάνει τιμή μέσω κατάλληλης μεθόδου set, η αντίστοιχη μέθοδος θα ήταν ως εξής: public void setcourse(course c) { mycourse = c;

Αλέξανδρος Χατζηγεωργίου, 2012 Για να συσχετίσουμε ένα συγκεκριμένο αντικείμενο Student με ένα αντικείμενο Course θα πρέπει, αφού δημιουργηθούν και τα 2 αντικείμενα, να καλέσουμε τη μέθοδο setcourse() επί του αντικειμένου Student μεταβιβάζοντας ως όρισμα το αντικείμενο Course. Για παράδειγμα στη main: public class Main { public static void main(string[] args) { Student S1 = new Student("John", "1209"); Student S2 = new Student("Nick"); Course C1 = new Course("Java", 5); S1.setMyCourse(C1); S2.setMyCourse(C1); //δημιουργία σύνδεσης Student S1->Course C1 S2.printInfo(); S2.printInfo(); Μετά την εκτέλεση της εντολής S1.setMyCourse(C1); η αναφορά mycourse του αντικειμένου S1 "δείχνει" πλέον στο αντικείμενο C1. Από το σημείο αυτό και μετά υφίσταται σύνδεση (link) μεταξύ των δύο αντικειμένων. Επισημαίνεται ότι οι συσχετίσεις υφίστανται στατικά μεταξύ κλάσεων και δημιουργούν την "υποδομή" για τη σύνδεση αντικειμένων. Απλώς και μόνο η ύπαρξη μια συσχέτισης δεν οδηγεί αυτομάτως και σε δημιουργία συνδέσεων μεταξύ των αντικειμένων. Οι συνδέσεις δημιουργούνται δυναμικά κατά τη διάρκεια της εκτέλεσης και μπορούν να μεταβάλλονται ή να καταστρέφονται. (για παράδειγμα η αναφορά mycourse του αντικειμένου S1 μπορεί να τροποποιηθεί στη συνέχεια ώστε να "δείχνει" σε άλλο μάθημα). Ένα αντικείμενο τύπου Student μπορεί να αξιοποιήσει τη γνώση του συσχετισμένου μαθήματος ώστε να αποστείλει μηνύματα σε αυτό είτε για την εκτέλεση λειτουργιών είτε για την άντληση δεδομένων. Στον παρακάτω πλήρη κώδικα της κλάσης Student η μέθοδος printinfo έχει τροποποιηθεί ώστε μαζί με τα στοιχεία του φοιτητή να εκτυπώνονται και στα στοιχεία του συσχετιζόμενου μαθήματος. public class Student { private String name; private String id; private Course mycourse; //αναφορά προς αντικείμενο τύπου Course public Student(String text1, String text2) { name = text1; id = text2; public Student(String text1) { name = text1; id = "9999";

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής public void setmycourse(course c) { mycourse = c; public String getname() { return name; public String getid() { return id; public void printinfo() { System.out.println("Name: " + name); System.out.println("AM: " + id); System.out.println("Course: " + mycourse.getname()); System.out.println("Credits: " + mycourse.getcredits()); Δημιουργία αμφίδρομης συσχέτισης με πολλαπλότητα Στην περίπτωση που θέλουμε να μοντελοποιήσουμε και το ότι ένα μάθημα "γνωρίζει" τους φοιτητές που είναι εγγεγραμμένοι σε αυτό θα πρέπει να δημιουργήσουμε και μια συσχέτιση αντίθετης φοράς (από την κλάση Μάθημα προς την κλάση Φοιτητής). Επιπλέον, επειδή στη συνήθη περίπτωση πολλοί φοιτητές εγγράφονται σε ένα μάθημα, η αντίστοιχη συσχέτιση πρέπει να έχει πολλαπλότητα "πολλά", που υποδηλώνει ότι ένα αντικείμενο τύπου Μάθημα μπορεί να συσχετιστεί με πολλά αντικείμενα τύπου Φοιτητής. Σε επίπεδο διαγράμματος κλάσεων μια τέτοια αμφίδρομη συσχέτιση απεικονίζεται ως εξής (το σύμβολο '*' υποδηλώνει "πολλά": - name - id Student - name - credits Course * Για λόγους απλούστευσης του διαγράμματος, όταν μια συσχέτιση είναι αμφίδρομη δεν απεικονίζεται ως δύο διαφορετικές ακμές αλλά ως μια γραμμή χωρίς βέλη (υπονοούνται βέλη και προς τις δύο κατευθύνσεις). Επίσης, για την απλοποίηση του παραδείγματος υποθέτουμε ότι σε ένα μάθημα μπορούν να εγγραφούν το πολύ 100 φοιτητές (από 0 έως 100) και κατά συνέπεια το σύμβολο της πολλαπλότητας τροποποιείται ανάλογα: - name - id Student * - name - credits Course

Αλέξανδρος Χατζηγεωργίου, 2012 Πώς υλοποιείται όμως μια τέτοια συσχέτιση από την κλάση Course προς την κλάση Student; Ακριβώς όπως η συσχέτιση από την κλάση Student προς την κλάση Course υλοποιήθηκε ως μια αναφορά τύπου Course μέσα στην κλάση Student, κατά ανάλογο τρόπο απαιτείται η προσθήκη πολλών αναφορών τύπου Student μέσα στην κλάση Course. Προφανώς δεν έχει νόημα η δημιουργία 100 ιδιοτήτων τύπου Student, αλλά η δημιουργία ενός πίνακα 100 θέσεων όπου κάθε θέση είναι μια αναφορά που μπορεί εν δυνάμει να "δείξει" προς ένα αντικείμενο τύπου Student. Αν δηλαδή θεωρήσουμε ένα αντικείμενο Course μπορούμε να θεωρήσουμε ότι φιλοξενεί έναν πίνακα αναφορών προς Student, που επιτρέπει στο μάθημα να συσχετιστεί με έως 100 φοιτητές, όπως φαίνεται στο παρακάτω σχήμα: John 1209... Java 5 Bob 1110 Σε επίπεδο κώδικα απαιτείται η προσθήκη μιας ιδιότητας στην κλάση Course υπό μορφή πίνακα 100 θέσεων όπου κάθε θέση αποτελεί αναφορά προς αντικείμενα Student: private Student[] enrolledstudents = new Student[100]; Τονίζεται για ακόμα μια φορά η αναγκαιότητα χρήσης ονομάτων που εκφράζουν όσο το δυνατόν περισσότερο την σημασία κάθε μεταβλητής. Επίσης, αξίζει να σημειωθεί ότι ένας πίνακας αποτελεί αντικείμενο στην Java και κατά συνέπεια πριν από τη χρήση του πίνακα απαιτείται η κατασκευή του αντίστοιχου αντικειμένου με χρήση της δεσμευμένης λέξης new. Όπως ακριβώς η κλάση Student είχε εφοδιαστεί με μέθοδο (setmycourse) που επέτρεπε στα αντικείμενα άλλων κλάσεων να θέτουν τιμή στην αναφορά mycourse ενός φοιτητή (και ουσιαστικά να συνδέουν έναν φοιτητή με ένα μάθημα), και η κλάση Course θα πρέπει να εφοδιαστεί με μέθοδο που να επιτρέπει την καταχώρηση τιμών στον πίνακα enrolledstudents (ουσιαστικά να καταχωρούν φοιτητές σε ένα μάθημα). Λόγω της χρήσης του πίνακα απαιτείται η ύπαρξη ενός μετρητή εγγεγραμμένων φοιτητών, έτσι ώστε κάθε νέος φοιτητής να εγγράφεται στην επόμενη ελεύθερη θέση του πίνακα enrolledstudents. Για το λόγο αυτό προστίθεται μια ιδιότητα counter στην κλάση Μάθημα που αντιπροσωπεύει τον αριθμό των ήδη εγγεγραμμένων φοιτητών: private int counter = 0; //δηλώνουμε ρητά ότι ο μετρητής μηδενίζεται κατά //την κατασκευή ενός μαθήματος (αν ο μηδενισμός μιας //int μεταβλητής είναι εγγυημένος στην Java) Η μέθοδος καταχώρησης ενός φοιτητή σε μάθημα, σε απλή μορφή (χωρίς τον έλεγχο της υπέρβασης των διαθέσιμων θέσεων του πίνακα) θα μπορούσε να γραφεί ως εξής: public void addstudent(student s) {

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής enrolledstudents[counter] = s; counter++; //καταχώρηση της τιμής της αναφοράς s //στην επόμενη ελεύθερη θέση του πίνακα //αύξηση του μετρητή ώστε να δείχνει στην //επόμενη ελεύθερη θέση Έχοντας στη διάθεσή μας μια συσχέτιση μεταξύ Μαθήματος και Φοιτητών, μπορούμε στη μέθοδο main (ή στον κώδικα οποιασδήποτε άλλης κλάσης) να δημιουργήσουμε αντικείμενα τύπου φοιτητής, να δημιουργήσουμε αντικείμενα τύπου Μάθημα και να εγγράψουμε φοιτητές σε κάποιο μάθημα, αποστέλλοντας το μήνυμα addstudent() στο αντίστοιχο αντικείμενο Μάθημα, μεταβιβάζοντας ως όρισμα το αντικείμενο Φοιτητή (την αναφορά προς αυτό): public class Main { public static void main(string[] args) { Student S1 = new Student("John", "1209"); Student S2 = new Student("Nick", "1709"); Course C1 = new Course("Math101"); S1.setMyCourse(C1); S2.setMyCourse(C2); C1.addStudent(S1); C1.addStudent(S2); από τις εντολές C1.addStudent(S1); C1.addStudent(S2); και μετά, το αντικείμενο C1 (μάθημα Math101) είναι συνδεδεμένο με τους φοιτητές S1 και S2, και κατά συνέπεια μπορεί να αποστείλει μηνύματα σε αυτά. Για παράδειγμα, μπορούμε να εφοδιάσουμε την κλάση Μάθημα με μια μέθοδο εκτύπωσης των στοιχείων των φοιτητών που είναι εγγεγραμμένοι σε ένα μάθημα. Για την υλοποίηση της λειτουργίας, απαιτείται η αλληλεπίδραση του μαθήματος με τους εγγεγραμμένους φοιτητές (αποστέλλοντας μηνύματα) ώστε να προκληθεί η εκτύπωση του ονόματος και του αριθμού μητρώου κάθε φοιτητή. Δεδομένου ότι οι φοιτητές μπορεί να είναι πολλοί, απαιτείται σάρωση της δομής δεδομένων που περιλαμβάνει τις αναφορές προς τους εγγεγραμμένους φοιτητές και την αποστολή κατάλληλου μηνύματος εκτύπωσης των στοιχείων. Η αντίστοιχη μέθοδος στην κλάση Course είναι: public void printstudentsinfo() { for(int i=0; i<counter; i++) { enrolledstudents[i].printinfo(); Δημιουργία αμφίδρομης συσχέτισης με πολλαπλότητα - χρήση δομής ArrayList

Αλέξανδρος Χατζηγεωργίου, 2012 Οι πίνακες ως δομές δεδομένων έχουν δύο μειονεκτήματα: είναι στατικοί ως προς το μέγεθος (δηλαδή το μέγεθός τους δεν μπορεί να μεταβληθεί κατά τη διάρκεια της εκτέλεσης του προγράμματος) και είναι στατικοί ως προς τον τύπο δεδομένων (δηλαδή αν δηλώσουμε ότι ένας πίνακας περιλαμβάνει στοιχεία του τύπου Α, δεν μπορούμε στην πορεία να εισάγουμε στοιχεία άλλου τύπου). H Java διαθέτει μια πλούσια συλλογή ισχυρών, καλά σχεδιασμένων και αποδοτικών δομών δεδομένων (Collection API) που ενδείκνυνται για όλα σχεδόν τα προβλήματα που μπορεί να αντιμετωπίσει ένας προγραμματιστής. Η πλέον συνηθισμένη και εύκολη δομή δεδομένων είναι η ArrayList που συμπεριφέρεται ως δυναμικός πίνακας (άρα είναι εύκολη στη χρήση) και επιπλέον δεν ισχύουν οι 2 περιορισμοί των πινάκων. Το μέγεθος της δομής μεταβάλλεται αυτόματα (μεγαλώνει) αν παραστεί ανάγκη κατά την εκτέλεση του προγράμματος. Για να χρησιμοποιήσουμε ένα ArrayList πρέπει να δημιουργήσουμε ένα στιγμιότυπο (αντικείμενο) της αντίστοιχης κλάσης: ArrayList enrolledstudents = new ArrayList(); Σε μια δομή τύπου ArrayList μπορούν να καταχωρηθούν αντικείμενα οποιουδήποτε τύπου (δεδομένου ότι οποιαδήποτε κλάση στην Java είναι υποκλάση της Object). Το πρόβλημα με αυτή την "ελευθερία" είναι ότι σε περίπτωση που "εξαχθεί" ένα αντικείμενο από μια θέση της δομής απαιτείται ρητή μετατροπή τύπου ώστε να μπορέσουμε να "χρησιμοποιήσουμε" το αντικείμενο. Π.χ. αν θέλουμε να εξάγουμε το 6ο στοιχείο μιας δομής στην οποία έχουν καταχωρηθεί αντικείμενα τύπου Student θα πρέπει να γίνει μετατροπή τύπου ως εξής: Student sixthstudent = (Stundet) enrolledstudents.get(5); Κάτι τέτοιο δεν είναι πάντοτε βολικό, ειδικά για τις περιπτώσεις όπου γνωρίζουμε ότι σε μια δομή ArrayList θα καταχωρηθούν αντικείμενα ενός μόνο τύπου. Για το λόγο αυτό μπορούμε να αξιοποιήσουμε τη δυνατότητα που παρέχεται από την έκδοση της Java 1.5 και μετά των generics (γενικευμένων τύπων) που επιτρέπουν να δηλώσουμε μια δομή δεδομένων υποδεικνύοντας τον τύπο δεδομένων των στοιχείων που θα περιλαμβάνουν. Στην περίπτωση αυτή δεν απαιτείται μετατροπή τύπου κατά την εξαγωγή στοιχείων και επιπλέον δεν επιτρέπεται η εισαγωγή στοιχείων άλλου τύπου (type safety). Η δήλωση είναι ως εξής: ArrayList<Student> enrolledstudents = new ArrayList<Student>(); Για την καταχώρηση ενός στοιχείου σε μια δομή ArrayList χρησιμοποιείται η μέθοδος add() και για την εξαγωγή ενός στοιχείου που βρίσκεται στη θέση με αριθμοδείκτη i η μέθοδος get(i). Για την εύρεση του πλήθους στοιχείων μιας δομής ArrayList διατίθεται η μέθοδος size(). Επομένως, ο πλήρης κώδικας της κλάσης Course με τη χρήση ArrayList είναι: import java.util.*; public class Course { private String name; private ArrayList<Student> enrolledstudents; private int counter;

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής public Course(String text) { name = text; enrolledstudents = new ArrayList<Student>(); counter = 0; public void addstudent(student s) { enrolledstudents.add(s); public void printstudentsinfo() { System.out.println("Course: " + name); System.out.println("--------"); for(int i=0; i<enrolledstudents.size(); i++) { Student s = enrolledstudents.get(i); s.printinfo(); public String getname() { return name; Χρήση του δείκτη this Στο ανωτέρω πρόγραμμα κάθε φοιτητής διατηρεί μια αναφορά προς το μάθημα που παρακολουθεί και κάθε Μάθημα προς τους φοιτητές που είναι εγγεγραμμένοι σε αυτό. Κατά συνέπεια απαιτείται η καταχώρηση της αναφοράς ενός μαθήματος σε έναν φοιτητή και επίσης η καταχώρηση των φοιτητών που παρακολουθούν ένα Μάθημα. Προφανώς κάτι τέτοιο θα ήταν άνευ νοήματος σε ένα πραγματικό σύστημα και το ζητούμενο θα ήταν για παράδειγμα, κατά την εγγραφή ενός φοιτητή σε ένα μάθημα, αυτομάτως να ενημερώνεται και ο φοιτητής για το μάθημα που παρακολουθεί. Αυτό επιτυγχάνεται σε αντικειμενοστρεφείς γλώσσες όπως η Java μόνο μέσω της αναφοράς this. Κάθε αντικείμενο διατηρεί μια αναφορά που "δείχνει" προς το ίδιο το αντικείμενο. Η αναφορά αυτή λαμβάνεται από τη δεσμευμένη λέξη this. Με άλλα λόγια, όταν εκτελείται κώδικας επί ενός αντικειμένου (μέσα σε κάποια μέθοδο) η αναφορά this δείχνει πάντοτε προς το ίδιο το αντικείμενο, δηλαδή το αντικείμενο στο οποίο εκτελείται ο κώδικας. Επομένως, κάθε αντικείμενο έχει γνώση της αναφοράς του "εαυτού του" την οποία μπορεί να μεταβιβάσει σε άλλα αντικείμενα, αν θέλει να τους γνωστοποιήσει τη διεύθυνσή του. Στα πλαίσια του ανωτέρου παραδείγματος, μέσα στον κώδικα της μεθόδου addstudent(student s) στην κλάση Course, μετά την εγγραφή του φοιτητή στο μάθημα: enrolledstudents.add(s); υπάρχει η δυνατότητα αποστολής της αναφοράς this (που σύμφωνα με όσα αναφέρθηκαν θα "δείχνει" στο αντικείμενο Μάθημα όπου εκείνη τη στιγμή εκτελείται η εγγραφή φοιτητών) στον αντίστοιχο φοιτητή (που είναι γνωστός μέσω της αναφοράς s που λαμβάνεται ως παράμετρος). Κατά συνέπεια αποστέλλοντας το μήνυμα setmycourse() προς τον φοιτητή s, με παράμετρο την αυτοαναφορά this, γνωστοποιούμε στον φοιτητή ποιο είναι το μάθημα και ο φοιτητή αυτομάτως καταχωρεί το μάθημα στην αναφορά mycourse που διαθέτει.

Αλέξανδρος Χατζηγεωργίου, 2012 Με αυτόν τον τρόπο οποτεδήποτε καλείται η μέθοδος addstudent() επί ενός Μαθήματος, επιτυγχάνονται και οι 2 στόχοι, δηλαδή αφενός η εγγραφή του φοιτητής στο Μάθημα και αφετέρου η σύνδεση του Φοιτητή με το αντίστοιχο μάθημα. Ο κώδικας της μεθόδου addstudent() θα είναι ως εξής: public void addstudent(student s) { enrolledstudents.add(s); s.setmycourse(this); Οι σχετικές ενέργειες θα μπορούσαν να αναπαρασταθούν γραφικά ως εξής: John 1209 S1 2: καταχώρηση της αναφοράς S1 στη λίστα φοιτητών 3: αποστολή μηνύματος setmycourse(this) 4: καταχώρηση της αναφοράς this στην αναφορά προς Μάθημα του φοιτητή S1... Java 5 this 1: αποστολή μηνύματος addstudent(s1)

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ 8.2 Δημιουργία μιας απλής γραφικής διασύνδεσης χρήστη 8.2.1 Δημιουργία παραθύρων Η δημιουργία γραφικής διασύνδεσης χρήστη, στην απλούστερη εκδοχή της, περιλαμβάνει την εμφάνιση γραφικών στην οθόνη (παραθύρου, κειμένου, εικόνων) και στοιχείων με τα οποία μπορεί να αλληλεπιδράσει ο χρήστης (πλήκτρα, μενού, πεδία κειμένου) καθώς και το χειρισμό των συμβάντων που προκαλούνται από τις ενέργειες του χρήστη (π.χ. πάτημα ενός πλήκτρου, μετακίνηση της δεικτικής συσκευής). Η δημιουργία εφαρμογής που περιλαμβάνει γραφική διασύνδεση χρήστη μπορεί να απλοποιηθεί αξιοποιώντας τη βιβλιοθήκη Swing είτε τη βιβλιοθήκη AWT (Abstract Windowing Toolkit) της Java. Ωστόσο, η βιβλιοθήκη Swing είναι νεότερη και είναι καλό να προτιμάται. Όλα τα γραφικά συστατικά που εμφανίζονται σε μια γραφική διασύνδεση έχουν ως κοινή υπερκλάση την κλάση javax.swing.jcomponent ή την java.awt.component, για τις βιβλιοθήκες swing και awt αντίστοιχα. Λόγω του πλήθους των διαθέσιμων κλάσεων στις βιβλιοθήκες αυτές, ο ενδιαφερόμενος αναγνώστης μπορεί να βρει την πλήρη τεκμηρίωση στις σελίδες της Java (http://java.sun.com). Η γραφική διασύνδεση του συστήματος διαχείρισης παραγγελιών βασίζεται σε οθόνες που στη Java υλοποιούνται ως παράθυρα. Ο ευκολότερος τρόπος για την υλοποίηση ενός παραθύρου είναι η δημιουργία ενός αντικειμένου της κλάσης JFrame. Ωστόσο, επειδή σε κάθε παράθυρο επιθυμούμε να τοποθετήσουμε διαφορετικά συστατικά, η ΠΛΗ 24 Σχεδιασμός Λογισμικού 150

Σύστημα Διαχείρισης Παραγγελιών προσέγγιση που ακολουθείται είναι η δημιουργία υποκλάσεων της JFrame, μιας για κάθε οθόνη της εφαρμογής. Η εμφάνιση ενός παραθύρου απαιτεί την κλήση του κατασκευαστή του (π.χ. από τη main). Στον ίδιο τον κατασκευαστή της κλάσης που υλοποιεί το παράθυρο προσθέτουμε την κλήση των εξής μεθόδων: - της μεθόδου setsize() με όρισμα το επιθυμητό μέγεθος του παραθύρου (σε pixels), - της μεθόδου settitle() για τον καθορισμό του τίτλου του παραθύρου, - της μεθόδου setvisible() με όρισμα true για την εμφάνιση του παραθύρου - της μεθόδου setdefaultcloseoperation() με όρισμα τη σταθερά JFrame.EXIT_ON_CLOSE έτσι ώστε με την επιλογή του συμβόλου "X" στο άνω δεξί τμήμα του παραθύρου, το παράθυρο να κλείνει Στο ακόλουθο δείγμα κώδικα παρουσιάζεται η κλάση MainFrame που υλοποιεί το απλό παράθυρο που παρουσιάζεται στο σχήμα 8.2. 151 ΠΛΗ 24 Σχεδιασμός Λογισμικού

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ import javax.swing.jframe; public class MainFrame extends JFrame { public MainFrame() { this.setsize(200, 200); this.settitle("κύρια Οθόνη Συστήματος Διαχείρισης Παραγγελιών"); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); Σχήμα 8.2: Παράθυρο με χρήση της JFrame 8.2.2 Εισαγωγή γραφικών συστατικών Κάθε παράθυρο αντικείμενο κλάσης που κληρονομεί την JFrame περιέχει έναν υποδοχέα (αντικείμενο της κλάσης Container) επί του οποίου μπορούν να τοποθετηθούν γραφικά συστατικά. Η προσέγγιση που χρησιμοποιήθηκε στην παρούσα μελέτη είναι η δημιουργία ενός ξεχωριστού πάνελ (αντικειμένου της κλάσης JPanel επί του οποίο επίσης μπορούν να τοποθετηθούν γραφικά συστατικά) και η τοποθέτηση του ΠΛΗ 24 Σχεδιασμός Λογισμικού 152

Σύστημα Διαχείρισης Παραγγελιών πάνελ στον υποδοχέα του παραθύρου με την εντολή this.setcontentpane(panel) στον κατασκευαστή κάθε παραθύρου. Εστιάζοντας για παράδειγμα στην Κύρια Οθόνη της εφαρμογής, τα συστατικά που επιθυμούμε να προσθέσουμε είναι πλήκτρα, στοιχεία που υλοποιούνται ως αντικείμενα της κλάσης JButton. Τα πλήκτρα δηλώνονται ως ιδιότητες της κλάσης MainFrame και δημιουργούνται στιγμιότυπά τους στον κατασκευαστή. Μετά τη δημιουργία των πλήκτρων (και την εισαγωγή του κειμένου που εμφανίζεται σε αυτά ως όρισμα στους κατασκευαστές τους), τα πλήκτρα τοποθετούνται στο πάνελ με χρήση της μεθόδου add() (το πάνελ με τη σειρά του έχει τοποθετηθεί στον υποδοχέα του παραθύρου) και με τον τρόπο αυτό εμφανίζονται στην οθόνη. Το ακόλουθο τμήμα κώδικα παράγει το παράθυρο που φαίνεται στο σχήμα 8.3. import javax.swing.*; public class MainFrame extends JFrame { //δήλωση πλήκτρων ως ιδιότητες private JButton createplatebutton; private JButton addorderbutton; private JButton prepareorderbutton; private JButton calculatetimebutton; private JPanel panel; public MainFrame() { //δημιουργία πλήκτρων createplatebutton = new JButton("Δημιουργία Πιάτου"); addorderbutton = new JButton("Προσθήκη Παραγγελίας"); prepareorderbutton = new JButton("Ετοιμασία Παραγγελίας"); calculatetimebutton = new JButton("Υπολογισμός Χρόνου"); 153 ΠΛΗ 24 Σχεδιασμός Λογισμικού

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ //δημιουργία πάνελ panel = new JPanel(); //προσθήκη πλήκτρων στο πάνελ panel.add(createplatebutton); panel.add(addorderbutton); panel.add(prepareorderbutton); panel.add(calculatetimebutton); //τοποθέτηση πάνελ στον υποδοχέα του παραθύρου this.setcontentpane(panel); this.setsize(500, 200); this.settitle("κύρια Οθόνη Συστήματος Διαχείρισης Παραγγελιών"); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); Σχήμα 8.3: Παράθυρο με αντικείμενα της κλάσης JButton ΠΛΗ 24 Σχεδιασμός Λογισμικού 154

Σύστημα Διαχείρισης Παραγγελιών Στο παράθυρο είναι επίσης δυνατό να προστεθεί μια εικόνα που υπάρχει ήδη ως αρχείο GIF ή JPEG ως εξής: ImageIcon image = new ImageIcon("cook.gif"); JLabel imagelabel = new JLabel(image); Η πρώτη εντολή δημιουργεί ένα αντικείμενο της κλάσης ImageIcon μια εικόνα από ένα αρχείο εικόνας GIF. Η δεύτερη εντολή εμφανίζει την εικόνα προσαρτώντας την σε μία ετικέτα αντικείμενο της κλάσης JLabel. Η τοποθέτηση του αντικειμένου imagelabel σε ένα παράθυρο θα έχει ως αποτέλεσμα την εμφάνιση της εικόνας στο παράθυρο. Η διάταξη της θέσης των διαφόρων γραφικών συστατικών που τοποθετούνται σε ένα παράθυρο είναι δυνατόν να ελεγχθεί με χρήση κατάλληλων διαχειριστών διάταξης. Οι διαχειριστές διάταξης είναι αντικείμενα κλάσεων της βιβλιοθήκης AWT που μπορούν να ελέγξουν την διάταξη των στοιχείων σε οποιαδήποτε κλάση-υποδοχέα συστατικών. Για παράδειγμα, ο καθορισμός της χρήσης ενός αντικειμένου BorderLayout στην κατασκευή ενός αντικειμένου JPanel, όπως φαίνεται στην ακόλουθη εντολή: JPanel panel = new JPanel(new BorderLayout()); επιτρέπει να ορίζουμε τη θέση ενός συστατικού που προστίθεται στο αντικείμενο panel με βάση τις σταθερές BorderLayout.NORTH (βόρεια), BorderLayout.SOUTH (νότια), BorderLayout.EAST (ανατολικά), BorderLayout.WEST (δυτικά), BorderLayout.CENTER (κεντρικά). Όπως γίνεται φανερό, αυτός ο διαχειριστής διάταξης παρέχει 155 ΠΛΗ 24 Σχεδιασμός Λογισμικού

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ περιορισμένες δυνατότητες για έλεγχο της θέσης. Ωστόσο, υπάρχουν διάφοροι διαχειριστές διάταξης, όπως ο FlowLayout, GridLayout, GridBagLayout κλπ). Ο συνδυασμός πολλών πάνελ με διαφορετικούς διαχειριστές διάταξης επιτρέπει την απεικόνιση οποιαδήποτε γραφικής δομής συστατικών επιθυμεί ο προγραμματιστής. 8.2.3 Χειρισμός συμβάντων Πέρα από την εμφάνιση γραφικών συστατικών στην οθόνη, αυτό που είναι σημαντικό σε μια εφαρμογή που αλληλεπιδρά με το χρήστη είναι η απόκριση στις ενέργειες του χρήστη, τεχνική που είναι γνωστή ως προγραμματισμός οδηγούμενος από συμβάντα (event-driven programming). Ο χειρισμός των συμβάντων που προκαλούνται από το χρήστη απαιτεί τη στοιχειώδη γνώση της διαδικασίας που λαμβάνει χώρα όταν ο χρήστης πατήσει κάποιο πλήκτρο, ή κάποιο άλλο στοιχείο που αναμένεται να προκαλέσει κάποια ενέργεια στο εκτελούμενο πρόγραμμα. Για παράδειγμα, όταν ο χρήστης πατήσει κάποιο πλήκτρο ή μετακινήσει τη δεικτική συσκευή, ο διαχειριστής παραθύρων της Java (Java Window Manager) αποστέλλει ειδοποίηση στο πρόγραμμα ότι ένα συμβάν έχει λάβει χώρα. Στη γενική περίπτωση, ο διαχειριστής παραθύρων παράγει ένα τεράστιο αριθμό συμβάντων. Ωστόσο, κάθε εφαρμογή δεν ενδιαφέρεται για όλα τα συμβάντα που λαμβάνουν χώρα. Για το λόγο αυτό κάθε πρόγραμμα το οποίο πρόκειται να αλληλεπιδρά με το χρήστη, οφείλει να δηλώνει ρητά ποια συμβάντα ενδιαφέρεται να δεχθεί. Αυτό ΠΛΗ 24 Σχεδιασμός Λογισμικού 156

Σύστημα Διαχείρισης Παραγγελιών επιτυγχάνεται με τη χρήση αντικειμένων που λειτουργούν ως "ακροατές" για συγκεκριμένους τύπους συμβάντων. Οι ακροατές συνδέονται με συγκεκριμένα γραφικά συστατικά και "ακροώνται" συγκεκριμένους τύπους συμβάντων. Μόλις λάβει χώρα το αντίστοιχο συμβάν, ο ακροατής ενεργοποιείται και εκτελείται ο επιθυμητός κώδικας. Η φιλοσοφίας αυτής της διαδικασίας παρουσιάζεται στο ακόλουθο σχήμα: Σχήμα 8.4: Ακρόαση συμβάντων στη Java Οι ακροατές συμβάντων είναι κλάσεις που υλοποιούν συγκεκριμένες διασυνδέσεις, ανάλογα με το είδος των συμβάντων που πρόκειται να χειριστούν. Για παράδειγμα, μια κλάση- ακροατής για συμβάντα που προκαλούνται από το πάτημα ενός πλήκτρου πρέπει να υλοποιεί τη 157 ΠΛΗ 24 Σχεδιασμός Λογισμικού

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ διασύνδεση ActionListener. Η διασύνδεση, που αποτελεί τμήμα της βιβλιοθήκης java.awt.event είναι η εξής: public interface ActionListener { void actionperformed(actionevent event); Η διασύνδεση περιλαμβάνει μόνο μία μέθοδο, την actionperformed() που λαμβάνει ως παράμετρο ένα αντικείμενο τύπου ActionEvent. Το αντικείμενο αυτό (παρέχεται από τον διαχειριστή παραθύρων της Java) περιέχει πληροφορίες για το ίδιο το συμβάν, όπως για παράδειγμα το ποιο πλήκτρο πατήθηκε. Σε περίπτωση που κάποιο πρόγραμμα ενδιαφέρεται για τέτοιου τύπου συμβάντα, οφείλει να δημιουργήσει μια κλάση που υλοποιεί τη διασύνδεση ActionListener. Στην υλοποίηση των μεθόδων της διασύνδεσης (στην προκειμένη περίπτωση στην υλοποίηση της μεθόδου actionperformed()) τοποθετείται ο κώδικας που θέλουμε να εκτελείται όταν λαμβάνει χώρα το συμβάν. Στη συνέχεια οφείλει να δημιουργήσει έναν ακροατή, υλοποιώντας ένα αντικείμενο της κλάσης αυτής. Τέλος, για την εγκατάσταση του ακροατή στο πρόγραμμα απαιτείται η σύνδεσή του με την πηγή του συμβάντος. Πηγή ενός συμβάντος είναι το συστατικό της γραφικής διασύνδεσης (στην προκειμένη περίπτωση ένα πλήκτρο) που μπορεί να παράγει ένα σχετικό συμβάν. Με αυτή τη δυνατότητα, ένας ακροατής μπορεί να συνδεθεί με πολλές πηγές ΠΛΗ 24 Σχεδιασμός Λογισμικού 158

Σύστημα Διαχείρισης Παραγγελιών συμβάντων ή κάθε πηγή μπορεί να έχει διαφορετικό ακροατή. Η σειρά των ενεργειών που απαιτείται προγραμματιστικά ώστε ένα πρόγραμμα να έχει τη δυνατότητα χειρισμού συμβάντων απεικονίζεται γραφικά στο ακόλουθο σχήμα. Σχήμα 8.5: Ακολουθία ενεργειών για τον χειρισμό συμβάντων Στο παρακάτω τμήμα κώδικα που αφορά σε μια κλάση MyFrame που υλοποιεί ένα παράθυρο και περιλαμβάνει ένα πλήκτρο button (όπως φαίνεται στο δείγμα εκτέλεσης του σχήματος 8.6), έχει δημιουργηθεί μια κλάση-ακροατής (ButtonListener), έχει υλοποιηθεί ένας ακροατής listener ως στιγμιότυπο της κλάσης ButtonListener και έχει συνδεθεί με το πλήκτρο του παραθύρου ώστε να ακροάται συμβάντα που σχετίζονται με αυτό (μέσω κλήσης της μεθόδου addactionlistener() στο αντικείμενο button. Ο κώδικας που εκτελείται κάθε φορά που ο 159 ΠΛΗ 24 Σχεδιασμός Λογισμικού

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ χρήστης πατά το πλήκτρο περιλαμβάνεται στη μέθοδο actionperformed() της κλάσης ButtonListener. import javax.swing.*; import java.awt.event.*; public class MyFrame extends JFrame { private JButton button; private JPanel panel; public MyFrame() { button = new JButton("Press Me"); //υλοποίηση ακροατή ButtonListener listener = new ButtonListener(); //σύνδεση ακροατή με πηγή συμβάντων (πλήκτρο button) button.addactionlistener(listener); panel = new JPanel(); panel.add(button); this.setcontentpane(panel); this.pack(); this.settitle("myframe"); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); ΠΛΗ 24 Σχεδιασμός Λογισμικού 160

Σύστημα Διαχείρισης Παραγγελιών //δημιουργία κλάσης ακροατή class ButtonListener implements ActionListener { public void actionperformed(actionevent e) { JOptionPane.showMessageDialog(null, "Button Pressed"); Σχήμα 8.6: Δείγμα Εκτέλεσης παραθύρου με χειρισμό συμβάντων Στην περίπτωση που υπάρχουν πολλά πλήκτρα, όπου η επιλογή του καθενός προκαλεί την εκτέλεση διαφορετικών ενεργειών (όπως για παράδειγμα στο σύστημα διαχείρισης παραγγελιών) υπάρχουν οι εξής δύο δυνατότητες: 1) να χρησιμοποιηθούν διαφορετικές κλάσεις-ακροατές για κάθε πλήκτρο με διαφορετική υλοποίηση της μεθόδου actionperformed() σε κάθε μία και 2) να χρησιμοποιηθεί ένας κοινός ακροατής. Στην περίπτωση αυτή, στο σώμα της μεθόδου 161 ΠΛΗ 24 Σχεδιασμός Λογισμικού

ΕΛΛΗΝΙΚΟ ΑΝΟΙΚΤΟ ΠΑΝΕΠΙΣΤΗΜΙΟ actionperformed() που καλείται όταν επιλέγεται ένα πλήκτρο, μπορεί να αξιοποιηθεί η πληροφορία που μεταφέρεται από το ίδιο το συμβάν (η παράμετρος ActionEvent της μεθόδου) ώστε να διαπιστώνεται ποιο πλήκτρο ήταν η πηγή του συμβάντος (καλώντας τη μέθοδο getsource()). Η δεύτερη αυτή δυνατότητα υλοποιήθηκε στις κλάσεις της γραφικής διασύνδεσης του συστήματος διαχείρισης παραγγελιών. ΠΛΗ 24 Σχεδιασμός Λογισμικού 162

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής Κληρονομικότητα Η κληρονομικότητα (inheritance) αποτελεί έναν από τους χαρακτηριστικότερους μηχανισμούς των αντικειμενοστρεφών γλωσσών προγραμματισμού. Επιτρέπει την δημιουργία μιας νέας κλάσης απορροφώντας "κληρονομώντας" όλες τις ιδιότητες και μεθόδους μιας υπάρχουσας κλάσης. Με τον τρόπο αυτό μπορούμε να δημιουργούμε "ειδικότερες" κατηγορίες αντικειμένων προσθέτοντας επιπλέον χαρακτηριστικά ή λειτουργίες. Έστω για παράδειγμα η κλάση Employee που αναφέρεται σε έναν υπάλληλο επιχείρησης, με μοναδικές ιδιότητες (χάριν απλότητας) το όνομα και το μηνιαίο μισθό του υπαλλήλου, και μοναδική μέθοδο (πλην του κατασκευαστή) την printstatement() που εκτυπώνει το όνομα και το μηνιαίο μισθό: public class Employee { private String name; private double salary; public Employee(String text, double amount) { name = text; salary = amount; public void printstatement() { System.out.println("Employee: " + name + " with salary: " + salary); Αν θεωρήσουμε ότι το σύστημα πρέπει να διαχειρίζεται και μια ειδικότερη κατηγορία υπαλλήλου (π.χ. Διοικητικό Υπάλληλο) ο οποίος πέραν του ονόματος και του μισθού έχει επιπρόσθετα ως ιδιότητες τις ώρες υπερωριακής απασχόλησης ανά μήνα καθώς και την αμοιβή ανά ώρα υπερωρίας, μια λύση θα ήταν να "αντιγράψουμε" την κλάση Employee σε μια νέα κλάση και να προσθέσουμε τα επιπλέον χαρακτηριστικά. Ωστόσο, λαμβάνοντας υπόψη ότι στην ανάπτυξη λογισμικού αποφεύγουμε τη δημιουργία "κλώνων" (τμημάτων κώδικα που επαναλαμβάνονται) η ενδεικνυόμενη λύση είναι η χρήση κληρονομικότητας, δηλαδή η δημιουργία μιας νέας κλάσης AdministrativeEmployee που κληρονομεί την υπάρχουσα: public class AdministrativeEmployee extends Employee{ private int overtimehours; private double overtimewage; Η νέα κλάση "απορροφά" από την υπάρχουσα όλες τις ιδιότητες και τη συμπεριφορά της και μπορεί να δηλώσει επιπλέον ιδιότητες ή/και μεθόδους. Σε ένα διάγραμμα κλάσεων της UML ο συμβολισμός της κληρονομικότητας πραγματοποιείται με τη χρήση μιας προσανατολισμένης ακμής (με κλειστό τρίγωνο στο άκρο της κλάσης από την οποία κληρονομούμε) ως εξής:

Αλέξανδρος Χατζηγεωργίου, 2012 Employee - name - salary + printstatement() AdministrativeEmployee - overtimehours - overtimewage Η κλάση Employee από την οποία κληρονομεί η AdministrativeEmployee ονομάζεται υπερκλάση (και συνήθως απεικονίζεται ψηλότερα) ενώ η κλάση που κληρονομεί ιδιότητες και λειτουργίες ονομάζεται υποκλάση (σε άλλες γλώσσες χρησιμοποιούνται οι όροι "βασική κλάση/παράγωγη κλάση" ή και "γονική κλάση/κλάση απόγονος". Στη γλώσσα Java δεν επιτρέπεται η πολλαπλή κληρονομικότητα (δηλαδή μια κλάση δεν μπορεί να έχει περισσότερες από μία υπερκλάσεις), ενώ μπορεί να υπάρχει κληρονομικότητα και σε βάθος μεγαλύτερο του 1. Δηλαδή μια κλάση μπορεί να κληρονομεί μια κλάση, η οποία κληρονομεί από κάποια άλλη, κ.ο.κ. Ωστόσο, για λόγους συντηρησιμότητας, δεν συνιστάται η χρήση ιεραρχιών κληρονομικότητας μεγάλου βάθους. Σημειώνεται ότι στην Java οι κατασκευαστές δεν κληρονομούνται. Κατά συνέπεια, αν θέλουμε να δημιουργούνται αντικείμενα της κλάσης AdministrativeEmployee παρέχοντας όνομα, μισθό, ώρες υπερωρίας και υπερωριακή αμοιβή, θα πρέπει να εφοδιάσουμε την υποκλάση με κατάλληλο κατασκευαστή. public AdministrativeEmployee(String text, double amount, int hours, double wage) και εντός του σώματος του κατασκευαστή μπορούμε να αναθέσουμε αρχικές τιμές στις αντίστοιχες ιδιότητες: public AdministrativeEmployee(String text, double amount, int hours, double wage) { name = text; salary = amount; overtimehours = hours; overtimewage = wage; ωστόσο, μια ιδιότητα που έχει δηλωθεί με ιδιωτική ορατότητα (private), αν και κληρονομείται από την υποκλάση, δεν επιτρέπεται η πρόσβαση (ανάγνωση ή αλλαγή τιμής). Για να επιτραπεί η πρόσβαση σε ιδιότητες στις υποκλάσεις το προσδιοριστικό ορατότητας θα πρέπει να μεταβληθεί σε protected

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής (προστατευμένη) που υποδηλώνει ότι μια ιδιότητα δεν είναι προσπελάσιμη από αντικείμενα άλλων κλάσεων, επιτρέπεται όμως η πρόσβαση από αντικείμενα των υποκλάσεων. Μια εναλλακτική ιδιότητα θα ήταν η προσπέλαση των ιδιοτήτων της υπερκλάσης μέσω κατάλληλων δημόσια διαθέσιμων μεθόδων (get() και set()) που θα μπορούσε να περιλαμβάνει για το σκοπό αυτό η υπερκλάση. Μια τρίτη δυνατότητα είναι η κλήση του κατασκευαστή της υπερκλάσης ώστε η ανάθεση των τιμών των δύο πρώτων παραμέτρων στις ιδιότητας name και salary να πραγματοποιηθεί από τον κώδικα του κατασκευαστή της υπερκλάσης. Για την κλήση του κατασκευαστή χρησιμοποιείται η δεσμευμένη λέξη super() μεταβιβάζοντας τις αντίστοιχες παραμέτρους. Για τις υπόλοιπες δύο ιδιότητες που δηλώνονται στην υποκλάση η ανάθεση τιμών γίνεται κατά τα γνωστά: public AdministrativeEmployee(String text, double amount, int hours, double wage) { super(text, amount); overtimehours = hours; overtimewage = wage; Αν δεν προστεθεί οποιαδήποτε άλλη μέθοδος, η υποκλάση διατηρεί τις μεθόδους της υπερκλάσης (δηλαδή είναι απολύτως επιτρεπτό να αποστείλουμε το μήνυμα printstatement() σε αντικείμενο τύπου AdministrativeEmployee και ως αποτέλεσμα θα εκτυπωθεί το όνομα και ο μισθός). Αν όμως στόχος είναι, η εκτύπωση των μηνιαίων αποδοχών να πραγματοποιείται με διαφορετικό τρόπο για τα αντικείμενα της υποκλάσης (κάτι που είναι το συνηθέστερο δεδομένου ότι προφανώς θέλουμε να εκτυπώνονται και οι αποδοχές λόγω υπερωριακής απασχόλησης) τότε θα πρέπει να επικαλύψουμε ή να επανα-ορίσουμε τη μέθοδο printstatement() για την υποκλάση, δηλαδή να ορίσουμε τη μέθοδο με την ίδια υπογραφή όπως στην υπερκλάση αλλά με διαφορετική υλοποίηση: public void printstatement() { super.printstatement(); System.out.println("and extra income: " + (overtimehours * overtimewage)); H εντολή super.printstatement() έχει ως αποτέλεσμα την κλήση της μεθόδου printstatement() της υπερκλάσης. (θα ήταν αδύνατο να κληθεί η printstatement() της κλάσης AdministrativeEmployee δεδομένου ότι σε εκείνο το σημείο ορίζεται η ίδια η μέθοδος printstatement()). Χρησιμοποιώντας τη δεσμευμένη λέξη super και το όνομα μιας μεθόδου της υπερκλάσης μπορούμε να καλέσουμε οποιαδήποτε μέθοδο της υπερκλάσης. Πλέον, αν κληθεί η μέθοδος printstatement() επί ενός αντικειμένου AdministrativeEmployee θα κληθεί η δική του μέθοδος και όχι αυτή που κληρονομείται από την υπερκλάση.

Αλέξανδρος Χατζηγεωργίου, 2012 Αρχή της Υποκατάστασης Σύμφωνα με την αρχή της υποκατάστασης (substitution principle), οπουδήποτε χρησιμοποιείται σε ένα πρόγραμμα-πελάτη ένα αντικείμενο μιας υπερκλάσης, θα πρέπει να είναι απολύτως επιτρεπτό (και να μην δημιουργούνται προβλήματα εκτέλεσης ή εσφαλμένης συμπεριφοράς του προγράμματος) να χρησιμοποιούνται στη θέση του και αντικείμενα υποκλάσεων. Για παράδειγμα, αν σε μια κλάση χρησιμοποιούνται αντικείμενα της κλάσης Employee θα πρέπει να είναι απολύτως επιτρεπτό στη θέση των αντικειμένων Employee να χρησιμοποιηθούν αντικείμενα AdministrativeEmployee. Ο λόγος είναι ότι μια σχέση κληρονομικότητας αποτελεί μια σχέση τύπου "είναι" (is-a). Με άλλα λόγια, τα αντικείμενα των υποκλάσεων "είναι" και στιγμιότυπα των υπερκλάσεών τους. Για παράδειγμα, ένας Διοικητικός Υπάλληλος έχει προφανώς τα ειδικά χαρακτηριστικά ενός Διοικητικού Υπαλλήλου, δεν παύει όμως να είναι και ένας "Υπάλληλος" της γενικής κατηγορίας, περιλαμβάνοντας τις ιδιότητες και λειτουργίες ενός υπαλλήλου. Αν παραβιάζεται εννοιολογικά η αρχή αυτή για τη σημασία της σχέσης κληρονομικότητας, τότε η χρήση της κληρονομικότητας είναι εσφαλμένη. Με άλλα λόγια ένας Υπάλληλος δεν μπορεί κληρονομεί από μια κλάση "Επιχείρηση" δεδομένου ότι ένας Υπάλληλος δεν "είναι" και επιχείρηση. Στα πλαίσια της αρχής της υποκατάστασης σε μια αναφορά προς αντικείμενα της υπερκλάσης, π.χ. μια αναφορά Employee: Employee ref; που μπορούμε φυσικά να αναθέσουμε τις διευθύνσεις αντικειμένων τύπου Employee, δηλαδή: Employee ref = new Employee("John", 850); είναι απολύτως επιτρεπτό να αναθέσουμε και τις διευθύνσεις αντικειμένων τύπου AdministrativeEmployee: Employee ref = new AdministrativeEmployee("John", 850, 10, 15); Το ερώτημα που τίθεται είναι γιατί μπορεί να είναι χρήσιμη αυτή η δυνατότητα; Για να γίνει αντιληπτό, ας θεωρήσουμε ότι σε μια κλάση που χρησιμοποιεί την ιεραρχία Υπαλλήλων-Διοικητικών Υπαλλήλων (κλάσηπελάτης), έστω τη Main, διατηρούμε μια λίστα όλων των υπαλλήλων. Κανονικά θα έπρεπε να έχουμε μια ξεχωριστή λίστα για κάθε κατηγορία υπαλλήλων. Κάθε εργασία που θα έπρεπε να εκτελείται επί όλων των υπαλλήλων (π.χ. εκτύπωση της μισθοδοσίας τους καλώντας τη μέθοδο printstatement()) θα έπρεπε να πραγματοποιείται "σαρώνοντας" όλες τις δομές δεδομένων για κάθε κατηγορία υπαλλήλου. Σε ένα πραγματικό σύστημα οι εργασίες επί των υπαλλήλων θα ήταν πολλές και κατά συνέπεια θα έπρεπε σε όλα τα αντίστοιχα σημεία του κώδικα να γίνονται ενέργειες για όλες τις δομές. Το πραγματικό όμως πρόβλημα σχετίζεται με τη συντήρηση του λογισμικού: Αν προστεθούν νέες υποκατηγορίες υπαλλήλων θα έπρεπε να προσθέτουμε νέες δομές δεδομένων για να φιλοξενήσουν τα αντικείμενα της κάθε νέας υποκατηγορίας.

Αντικειμενοστρεφής Προγραμματισμός Τμήμα Εφαρμοσμένης Πληροφορικής Η λύση στο πρόβλημα είναι η χρήση μιας δομής δεδομένων τύπου Employee η οποία λόγω της αρχής της υποκατάστασης θα μπορεί να δεχθεί αντικείμενα τύπου Employee αλλά και αντικείμενα οποιαδήποτε υποκλάσης της Employee. Η μέθοδος main για παράδειγμα θα μπορούσε να είναι: public static void main(string[] args) { ArrayList<Employee> employees = new ArrayList<Employee>(); employees.add(new Employee("John", 900)); employees.add(new AdminEmployee("Bob", 800, 15, 10)); employees.add(new AdminEmployee("Nick", 700, 10, 12)); Πολυμορφισμός Αν στη συνέχεια επιθυμούμε την εκτέλεση της μεθόδου printstatement() σε κάθε είδος υπαλλήλου που περιλαμβάνεται στη δομή employees μπορούμε στη main να προσθέσουμε το κάτωθι τμήμα κώδικα που "σαρώνει" τη δομή των υπαλλήλων και αποστέλλει σε κάθε αντικείμενο της δομής το ίδιο μήνυμα printstatement(): for(int i=0; i<employees.size(); i++) employees.get(i).printstatement(); //πολυμορφική κλήση Η κλήση της μεθόδου printstatement() στο ανωτέρω for loop παρουσιάζει ιδιαίτερο ενδιαφέρον στα πλαίσια του αντικειμενοστρεφούς προγραμματισμού. H Java δεν γνωρίζει κατά τη διάρκεια της μεταγλώττισης (at compile time) τι αντικείμενο περιλαμβάνεται σε κάθε θέση της δομής employee. Κατά συνέπεια δεν γνωρίζει και τι αντικείμενο επιστρέφεται από την εκτέλεση της εντολής employees.get(i) σε κάθε επανάληψη (Υπάλληλος ή Διοικητικός Υπάλληλος). Για το λόγο αυτό δεν μπορεί να "αποφασίσει" κατά τη διάρκεια της μεταγλώττισης ποια μέθοδος printstatement() θα πρέπει να κληθεί (αυτή της κλάσης Employee ή αυτή της κλάσης AdminEmployee). Για την αντιμετώπιση του ζητήματος η Java "καθυστερεί" την απόφαση αυτή μέχρι την εκτέλεση του προγράμματος όπου είναι πλέον γνωστό τι αντικείμενο βρίσκεται σε κάθε θέση της δομής employees. Αν η Java διαπιστώσει ότι πρόκειται για αντικείμενο τύπου Employee καλείται η μέθοδος printstatement() της κλάσης Employee. Αν η Java διαπιστώσει ότι πρόκειται για αντικείμενο τύπου AdminEmployee καλείται η μέθοδος printstatement() της κλάσης AdminEmployee (που έχει διαφορετική υλοποίηση σύμφωνα με τα προηγούμενα). Η δυνατότητα αυτή της Java (και άλλων αντικειμενοστρεφών γλωσσών) ονομάζεται "δυναμική διασύνδεση" ή "καθυστερημένη διασύνδεση" μεθόδου και αντίστοιχης κλάσης (dynamic ή late binding). To όφελος που προκύπτει είναι εξαιρετικά σημαντικό: ουσιαστικά στον κώδικα του for αποστέλλουμε το ίδιο ακριβώς μήνυμα σε ένα πλήθος αντικειμένων (το μήνυμα printstatement()) και κάθε αντικείμενοπαραλήπτης ανταποκρίνεται με διαφορετικό τρόπο, αυτόν που αντιστοιχεί στην κλάση του αντικειμένου. (ένα μήνυμα -> πολλές μορφές απόκρισης). H δυνατότητα αυτή ονομάζεται πολυμορφισμός και η αντίστοιχη κλήση της μεθόδου "πολυμορφική κλήση". Χάρη στον πολυμορφισμό, αν μελλοντικά

Αλέξανδρος Χατζηγεωργίου, 2012 προστεθούν νέες υποκατηγορίες υπαλλήλων (π.χ. Τεχνικοί Υπάλληλοι, Εξωτερικοί Συνεργάτες, Πωλητές κλπ με ξεχωριστό τρόπο μισθοδοσίας και διαφορετική υλοποίηση της printstatement()), τότε προφανώς θα πρέπει να προστεθούν στο σύστημα οι αντίστοιχες κλάσεις, αλλά ο κώδικας του ανωτέρου τμήματος (ο βρόχος επανάληψης for) δεν θα πρέπει να τροποποιηθεί καθόλου. Δηλαδή ένα τμήμα κώδικα μπορεί να επεκτείνει τη λειτουργικότητά του (να χειριστεί και άλλες κατηγορίες υπαλλήλων) χωρίς να χρειάζεται η τροποποίησή του (Αρχή της Ανοικτής-Κλειστής Σχεδίασης). Φυσικά στο ανωτέρω παράδειγμα το τμήμα κώδικα που επωφελείται από τον πολυμορφισμό είναι μικρό (ο κώδικας του for) αλλά σε ένα πραγματικό σύστημα την ίδια "ανεξαρτησία" μπορούν να έχουν πλήθος από κλάσεις ακόμα και ολόκληρα πληροφοριακά συστήματα. Για να γίνει απολύτως κατανοητό ότι η Java δεν είναι σε θέση να γνωρίζει τι ακριβώς υπάρχει ή θα τοποθετηθεί σε κάθε θέση της δομής employees (και επομένως δεν μπορεί να ξέρει ποια μέθοδο πρέπει να καλέσει για κάθε αντικείμενο που βρίσκεται σε μια θέση της δομής) το πρόγραμμα θα μπορούσε να ρωτά τον χρήστη τι αντικείμενο επιθυμεί να εισάγει κάθε φορά: public static void main(string[] args) { ArrayList<Employee> employees = new ArrayList<Employee>(); Scanner scan = new Scanner(System.in); boolean more = true; while(more) { System.out.println("What kind of employee do you want to add? (1: Employee, 2 AdminEmployee)"); int answer = scan.nextint(); scan.nextline(); System.out.println("Enter name: "); String name = scan.nextline(); System.out.println("Enter salary: "); double salary = scan.nextdouble(); if(answer == 1) { employees.add(new Employee(name, salary)); else { System.out.println("Enter overtime hours: "); int hours = scan.nextint(); System.out.println("Enter overtime wage: "); double wage = scan.nextdouble(); employees.add(new AdminEmployee(name, salary, hours, wage)); scan.nextline(); //Ερώτημα για το αν θα επαναληφθεί εισαγωγή υπαλλήλου System.out.println("More employees? (y/n)"); String moredata = scan.nextline(); if(moredata.equals("n")) more = false;