ΑΡΧΕΣ ΣΧΕΔΙΑΣΗΣ. 5.1 Εισαγωγή



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

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

Εισαγωγή στη Σχεδίαση Λογισμικού

Ανάπτυξη & Σχεδίαση Λογισμικού (ΗΥ420)

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

Σχεδίαση Κλάσεων. Γρηγόρης Τσουµάκας. Τµήµα Πληροφορικής, Αριστοτέλειο Πανεπιστήµιο Θεσσαλονίκης. Έκδοση:

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

Έλεγχος Συνένωσης και Διασφάλιση Ποιότητας

ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΑΝΑΛΥΣΗ Πρότυπα Σχεδίασης. Ιωάννης Σταμέλος Βάιος Κολοφωτιάς Πληροφορική

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

Διαδικασίες παραγωγής λογισμικού. Βασικές αρχές Τεχνολογίας Λογισμικού, 8η αγγ. έκδοση

ΚΕΦΑΛΑΙΟ 5. Κύκλος Ζωής Εφαρμογών ΕΝΟΤΗΤΑ 2. Εφαρμογές Πληροφορικής. Διδακτικές ενότητες 5.1 Πρόβλημα και υπολογιστής 5.2 Ανάπτυξη εφαρμογών

Αρχιτεκτονική Λογισμικού

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

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

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

Πίνακας Περιεχομένων. μέρος A 1 Εισαγωγή στην Τεχνολογία Λογισμικού

ΚΕΦΑΛΑΙΟ 10 ΥΠΟΠΡΟΓΡΑΜΜΑΤΑ

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

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

ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΩΝ ΣΕ ΠΡΟΓΡΑΜΜΑΤΙΣΤΙΚΟ ΠΕΡΙΒΑΛΛΟΝ ΜΑΡΙΑ Σ. ΖΙΩΓΑ ΚΑΘΗΓΗΤΡΙΑ ΠΛΗΡΟΦΟΡΙΚΗΣ ΕΙΣΑΓΩΓΗ ΣΤΟΝ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ

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

ΕΡΓΟ: Συγκριτική Μελέτη Λογισμικού Βιβλιοθηκών, Λογισμικού Εφαρμογών Ανοικτού Κώδικα και Βιομηχανικού Λογισμικού MIS:

ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΩΝ ΣΕ Π ΡΟΓΡΑΜΜΑΤΙΣΤΙΚΟ Π ΕΡΙΒΑΛΛΟΝ

Προβλήματα, αλγόριθμοι, ψευδοκώδικας

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

ΠΑΝΕΠΙΣΤΗΜΙΟ ΑΙΓΑΙΟΥ

Αντικειμενοστρεφής Προγραμματισμός Διάλεξη 4 : CLASSES

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

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

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

Wrapper Classes, Abstract Classes and Interfaces

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

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

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

1 Ανάλυση Προβλήματος

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

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

ΠΑΝΕΠΙΣΤΗΜΙΟ ΑΙΓΑΙΟΥ

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

Διαδικασίες παραγωγής λογισμικού. Βασικές αρχές Τεχνολογίας Λογισμικού, 8η αγγ. έκδοση

Κεφάλαιο 10 ο Υποπρογράµµατα

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

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

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

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

Κωδικός: <Κωδ.Αρ.Εγγράφου/ΚωδικόΌνομαΈργου/Αρ. Έκδοσης> <Company Name> <Όνομα - Κωδικό Όνομα Έργου> Έγγραφο Περιγραφής Σχεδίου Λογισμικού

Διαδικασίες παραγωγής λογισμικού. I. Sommerville 2006 Βασικές αρχές Τεχνολογίας Λογισμικού, 8η αγγ. έκδοση Κεφ. 4

Διαδικασίες παραγωγής λογισμικού. I. Sommerville 2006 Βασικές αρχές Τεχνολογίας Λογισμικού, 8η αγγ. έκδοση Κεφ. 4

8 Τεχνικός Εφαρμογών Πληροφορικής με Πολυμέσα

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

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

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

1 Συστήματα Αυτοματισμού Βιβλιοθηκών

ΕΠΛ233 Βιβλιοθήκες και Προσδιοριστές Πρόσβασης στην JAVA

Δομημένος Προγραμματισμός

Σχηματίζοντας Γραφικές Παραστάσεις για Ημίτονο και Συνημίτονο και Ελέγχοντας Περιορισμούς σε Συστάδες Καρτών Τόμπολας

Σχεδιασµός βασισµένος σε συνιστώσες

ΣΧΕ ΙΑΣΗ ΑΝΤΙΚΕΙΜΕΝΩΝ ΜΕ ΑΡΜΟ ΙΟΤΗΤΕΣ. Ορισµός σχεδιαστικών προτύπων Εφαρµογή των 9 GRASP προτύπων

Τεχνολογία Λογισμικού

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

Κεφάλαιο 2ο. Κατανοώντας την αντικειμενοστρέφεια

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

ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΩΝ ΣΕ ΠΡΟΓΡΑΜΜΑΤΙΣΤΙΚΟ ΠΕΡΙΒΑΛΛΟΝ

ΑΝΑΛΥΣΗ ΑΠΑΙΤΗΣΕΩΝ ανάλυση απαιτήσεων Σε αυτό το μάθημα θα ασχοληθούμε με : Δημιουργία μοντέλων

ΥΠΟΛΟΓΙΣΤΙΚΕΣ ΤΕΧΝΙΚΕΣ ΓΙΑ ΣΥΣΤΗΜΑΤΑ ΜΕΤΑΔΟΣΗΣ ΠΛΗΡΟΦΟΡΙΑΣ

Μοντελοποίηση Πεδίου

ΥΠΟΠΡΟΓΡΑΜΜΑΤΑ. Κάθε υποπρόγραμμα έχει μόνο μία είσοδο και μία έξοδο. Κάθε υποπρόγραμμα πρέπει να είναι ανεξάρτητο από τα άλλα.

Βάσεις Δεδομένων. Εισαγωγή Ανάλυση Απαιτήσεων. Φροντιστήριο 1 ο

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

Κεφάλαιο 4 Σχεδίαση Βάσεων Δεδομένων

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

Αρχιτεκτονική σχεδίαση με ηλεκτρονικό υπολογιστή

Οι βασικές λειτουργίες (ή πράξεις) που γίνονται σε μια δομή δεδομένων είναι:

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

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

Μεταγλώττιση και σύνδεση πολλαπλών αρχείων κώδικα. Προγραμματισμός II 1

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

Δοκιμή και Αποσφαλμάτωση Testing and Debugging

2.1. Εντολές Σχόλια Τύποι Δεδομένων

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

Οδηγίες Συγγραφής και Αξιολόγησης Εργασιών του μαθήματος

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

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

TRAVIS TRAFFIC VIOLATION INFORMATION SYSTEM ΣΥΣΤΗΜΑ ΔΙΑΧΕΙΡΗΣΗΣ ΠΑΡΑΒΑΣΕΩΝ ΦΩΤΟΕΠΙΣΗΜΑΝΣΗΣ

Ε Ι Α Γ Ω Γ Η Σ Ο Ν Π Ρ Ο Γ Ρ Α Μ Μ Α Σ Ι Μ Ο Κ Ε Υ Α Λ Α Ι Ο 6. Σο πρόγραμμα γράφεται σε κάποια γλώσσα προγραμματισμού.

Τεχνικές σχεδίασης προγραμμάτων, Προγραμματιστικά Περιβάλλοντα

ΚΥΚΛΟΣ ΖΩΗΣ ΛΟΓΙΣΜΙΚΟΥ και ΔΙΑΓΡΑΜΜΑΤΑ ΡΟΗΣ ΔΕΔΟΜΕΝΩΝ

5 ΕΙΣΑΓΩΓΗ ΣΤΗ ΘΕΩΡΙΑ ΑΛΓΟΡΙΘΜΩΝ

Πληροφοριακά Συστήματα Διοίκησης Ενότητα 1: Βασικές Αρχές Αντικειμενοστραφούς Σχεδίασης Συστημάτων και Εφαρμογών (1ο Μέρος)

Υποδείγματα Ανάπτυξης

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

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

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

Πληροφορική 2. Τεχνολογία Λογισμικού

ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΑΝΑΛΥΣΗ Επιχειρηματική Μοντελοποίηση. Ιωάννης Σταμέλος Βάιος Κολοφωτιάς Πληροφορική

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

Προγραμματισμός Η/Υ. Συναρτήσεις & Υποπρογράμματα. ΤΕΙ Ιονίων Νήσων Τμήμα Τεχνολόγων Περιβάλλοντος Κατεύθυνση Τεχνολογιών Φυσικού Περιβάλλοντος

<<ΔΗΜΗΤΡΗΣ ΜΑΝΩΛΗΣ ΦΥΣΙΚΟΣ ΜCs>> 1

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

Transcript:

ΑΡΧΕΣ ΣΧΕΔΙΑΣΗΣ 5.1 Εισαγωγή Κατά τη διάρκεια της σχεδίασης οποιουδήποτε τεχνικού έργου, επιδιώκεται η αποσύνθεση του συστήματος σε τμήματα, η ανάθεση αρμοδιοτήτων σε κάθε τμήμα και η επικύρωση ότι όλα τα τμήματα μαζί επιτυγχάνουν τους σκοπούς του συστήματος. Στα πλαίσια του λογισμικού, η σχεδίαση είναι μια διαδικασία επίλυσης, της οποίας στόχος είναι η περιγραφή του τρόπου υλοποίησης των λειτουργικών απαιτήσεων, υπό τους περιορισμούς που θέτουν οι μή-λειτουργικές απαιτήσεις (συμπεριλαμβανομένων των περιορισμών κόστους και χρόνου) και η οποία συμμορφώνεται με συγκεκριμένες αρχές καλής ποιότητας. Η διαδικασία αυτή περιλαμβάνει συνήθως ένα πλήθος σχεδιαστικών προβλημάτων, σε κάθε ένα από τα οποία μπορούν να δοθούν πολλές εναλλακτικές λύσεις. Ο λόγος για τον οποίο η σχεδίαση συνδέεται συνήθως με την έννοια της γραφικής αναπαράστασης του συστήματος υπό τύπο διαγράμματος (αρχιτεκτονικό σχέδιο, ηλεκτρολογικό σχέδιο, διαγράμματα UML), είναι ότι σε μια γραφική αναπαράσταση οι διάφορες εναλλακτικές εξετάζονται και μπορούν να εναλλαχθούν χωρίς σημαντικό κόστος. Ένα αντικειμενοστρεφές σύστημα λογισμικού, μπορεί προφανώς να δομηθεί με πάρα πολλούς τρόπους, αν αναλογιστεί κανείς τον αριθμό των δυνατών κλάσεων, των λειτουργιών που μπορούν να περιλαμβάνουν καθώς και των δυνατών συσχετίσεων μεταξύ τους. Το κόστος της σχεδίασης στον κύκλο ζωής ενός προϊόντος λογισμικού είναι σημαντικό και εξαρτάται κυρίως από το μέγεθος του συστήματος και κατ' επέκταση από το πλήθος και είδος των απαιτήσεων. Ωστόσο, αυτό που καθιστά τη σχεδίαση ιδιαιτέρως σημαντική από πλευράς κόστους, είναι το ότι καθορίζει σε εξαιρετικά μεγάλο βαθμό, την ευκολία με την οποία μπορούν να πραγματοποιηθούν αλλαγές στο σύστημα. Επειδή δε το κόστος συντήρησης ενός μεγάλου έργου λογισμικού είναι συνήθως πολλαπλάσιο του κόστους ανάπτυξής του, γίνεται εύκολα κατανοητό, ότι όσο μεγαλύτερη πρόνοια ληφθεί κατά τη διάρκεια της σχεδίασης ώστε το σύστημα να επεκτείνεται και να τροποποιείται με μικρή προσπάθεια, τόσο οικονομικότερη γίνεται η εξέλιξη του λογισμικού. Παρόλο που είναι σχετικά δύσκολο να οριστεί τι συνιστά "καλή" αντικειμενοστρεφή σχεδίαση (χρησιμοποιώντας πρότυπα όπως το ISO 9001 και ISO 9126), είναι αρκετά ευ- 97

98 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ κολότερο να διατυπωθούν τα συμπτώματα μιας σχεδίασης "κακής" ποιότητας. Τα συμπτώματα που μπορεί να παρουσιαστούν είτε κατά την αρχική ανάπτυξη του λογισμικού, είτε πολύ περισσότερο κατά την εξέλιξη που υφίσταται στη διάρκεια ζωής του είναι τα εξής (Martin, 2003): 1. Δυσκαμψία (Rigidity): Το σύστημα είναι δύσκολο να τροποποιηθεί διότι κάθε αλλαγή σε κάποια μονάδα λογισμικού οδηγεί σε πληθώρα αλλαγών σε άλλες μονάδες του συστήματος. Με το πρόβλημα αυτό έρχονται πολύ συχνά αντιμέτωποι οι προγραμματιστές όταν, για μια φαινομενικά απλή αλλαγή αδυνατούν να εκτιμήσουν ορθά την απαιτούμενη προσπάθεια. Συνήθως πρόκειται για αλλαγές που πρέπει να γίνουν σε αλληλοεξαρτώμενες μονάδες λογισμικού, οι οποίες για να λειτουργήσουν ορθά, πρέπει να τροποποιηθούν καταλλήλως. 2. Ευθραυστότητα (Fragility): Οι αλλαγές που πραγματοποιούνται στο λογισμικό προκαλούν σφάλματα σε διάφορα σημεία. Συχνά, τα νέα προβλήματα ανακύπτουν σε περιοχές φαινομενικά άσχετες προς αυτήν στην οποία πραγματοποιήθηκε η αλλαγή. Η διόρθωση των νέων προβλημάτων δημιουργεί με τη σειρά της νέα προβλήματα και αποτυχίες του λογισμικού. Όσο το λογισμικό γίνεται πιο εύθραυστο, τόσο η πιθανότητα εμφάνισης νέων προβλημάτων πλησιάζει τη βεβαιότητα, με άλλα λόγια η λίστα των bugs δεν αδειάζει ποτέ. 3. Ακινησία (Immobility): Υπάρχει δυσκολία διαχωρισμού του συστήματος σε συστατικά τα οποία μπορούν να επαναχρησιμοποιηθούν σε άλλες εφαρμογές. Μια σχεδίαση χαρακτηρίζεται από ακινησία, αν περιλαμβάνει τμήματα που θα μπορούσαν να χρησιμοποιηθούν σε άλλες περιπτώσεις, αλλά η προσπάθεια και το ρίσκο που εμπλέκονται στο διαχωρισμό αυτών των τμημάτων είναι πολύ μεγάλα. 4. Έλλειψη ρευστότητας (Viscosity): Η πραγματοποίηση τροποποιήσεων με λάθος τρόπο είναι ευκολότερη από την πραγματοποίησή τους με τον ορθό τρόπο. Οι αλλαγές που μπορούν να γίνουν στο λογισμικό είναι εν γένει δυνατό να διατηρήσουν τη σχεδίαση του συστήματος (design preserving changes), ενώ άλλες αλλοιώνουν το σχέδιο υπό μορφή "τρυκ". Αν οι πρώτες αλλαγές πραγματοποιούνται δυσκολότερα από ότι οι δεύτερες, το λογισμικό παρουσιάζει μεγάλη έλλειψη ρευστότητας. 5. Περιττή Πολυπλοκότητα (Needless Complexity): Το λογισμικό περιλαμβάνει στοιχεία που δεν είναι (ούτε πρόκειται να γίνουν) χρήσιμα. Η ύπαρξη περίπλοκων δομών κώδικα, είτε λόγω αδυναμίας εύρεσης αποδοτικότερων δομών, είτε λόγω ενσωμάτωσης πιθανών μελλοντικών απαιτήσεων χρήστη, οδηγεί συνήθως σε εκφυλισμό της αρχικής σχεδίασης και σε δυσκολία κατανόησης του συστήματος. 6. Περιττή Επανάληψη (Needless Repetition): Η σχεδίαση περιλαμβάνει επαναλαμβανόμενες δομές που θα μπορούσαν να ενοποιηθούν υπό μία κοινή αφαίρεση. Οι λειτουργίες αντιγραφής και επικόλλησης (copy-paste) μπορεί να είναι χρήσιμες σε επίπεδο επεξεργασίας κειμένου ή κώδικα, αλλά έχουν συχνά καταστροφικές συνέπειες για το σύστημα. Όταν το ίδιο τμήμα κώδικα εμφανίζεται σε διάφορα σημεία με ελαφρά διαφοροποιημένες εκδόσεις, η ομάδα ανάπτυξης παραβλέπει συχνά την υπερκεί-

5 Αρχές σχεδίασης 99 μενη αφαίρεση και οι ενδεχόμενες αλλαγές πρέπει να πραγματοποιούνται σε κάθε σημείο ξεχωριστά. Η εύρεση όλων των επαναλαμβανόμενων τμημάτων και η εξάλειψή τους χρησιμοποιώντας μια κατάλληλη αφαίρεση, βελτιώνει αισθητά την ποιότητα του σχεδίου. 7. Αδιαφάνεια (Opacity): Δυσκολία κατανόησης μιας μονάδας (σε επίπεδο σχεδίου ή κώδικα). Ο κώδικας μπορεί να γραφεί με ξεκάθαρο και εκφραστικό τρόπο, είτε μπορεί να γραφεί με περίπλοκο και αδιαφανή τρόπο (υπάρχουν μάλιστα και παγκόσμιοι διαγωνισμοί όπου σκοπός είναι η συγγραφή κώδικα με τον πλέον "αδιαφανή" και δύσκολο να κατανοηθεί τρόπο!). Ο κώδικας που εξελίσσεται με την πάροδο του χρόνου, συνήθως γίνεται ολοένα και πιο αδιαφανής. Για την αποφυγή αυτού του χαρακτηριστικού απαιτείται συστηματική προσπάθεια συγγραφής κώδικα, έτσι ώστε αυτός να γίνεται εύκολα κατανοητός από τρίτους και ταυτοχρόνως συχνή επισκόπηση από άλλους προγραμματιστές. Όπως χιουμοριστικά αναφέρει ο Roberts (Roberts, 2004), Τεχνολογία Λογισμικού είναι "η δραστηριότητα συγγραφής προγραμμάτων τα οποία μπορούν να κατανοηθούν και να συντηρηθούν από άλλους", σε αντίθεση με τον προγραμματισμό που είναι "η δραστηριότητα συγγραφής προγραμμάτων τα οποία δεν μπορούν να κατανοηθούν και συντηρηθούν από κανέναν άλλο πλην του συγγραφέα". Στη συνέχεια αυτής της ενότητας θα αναλυθούν αρχές (principles) που πρέπει να διέπουν την αντικειμενοστρεφή σχεδίαση ενός συστήματος λογισμικού. Η παραβίαση μιας ή περισσοτέρων από αυτές τις αρχές οδηγεί σχεδόν με βεβαιότητα σε ένα ή περισσότερα από τα ανωτέρω συμπτώματα. Οι αρχές αυτές είναι: 1. Αρχή της Ανοικτής-Κλειστής Σχεδίασης 2. Αρχή της Ενσωμάτωσης 3. Αρχή της Χαμηλής Σύζευξης 4. Αρχή της Μοναδικής Αρμοδιότητας 5. Αρχή της Υποκατάστασης της Liskov 6. Αρχή της Αντιστροφής των Εξαρτήσεων 7. Αρχή του Διαχωρισμού των Διασυνδέσεων Οι ανωτέρω αρχές είναι προϊόν εμπειρίας δεκαετιών στην τεχνολογία λογισμικού. Δεν είναι το αποτέλεσμα της σκέψης ενός μοναδικού προγραμματιστή, αλλά αναπαριστούν την ολοκλήρωση των ιδεών και συστάσεων μεγάλου αριθμού ατόμων. Παρόλο που εδώ εμφανίζονται ως αρχές αντικειμενοστρεφούς σχεδίασης, στην ουσία πρόκειται για ειδικές περιπτώσεις καθιερωμένων αρχών της τεχνολογίας λογισμικού. Οι αρχές της Ενσωμάτωσης και της Χαμηλής Σύζευξης είναι γενικές και καταγράφονται με διαφορετικούς τρόπους στη βιβλιογραφία. Οι υπόλοιπες πέντε έχουν καταγραφεί συστηματικά από τον Martin (Martin, 2003). Αξίζει να σημειωθεί ότι η εφαρμογή των ανωτέρω αρχών δεν πραγματοποιείται διαρκώς και χωρίς λόγο: Είναι λάθος να εφαρμόζεται μια αρχή μόνο γιατί υπάρχει. Οι αρχές

100 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ εφαρμόζονται όταν παρατηρηθούν κάποια από τα συμπτώματα, ως τρόπος "θεραπείας" τους. 5.2 Αρχή της Ανοικτής-Κλειστής Σχεδίασης "Όλα τα συστήματα λογισμικού αλλάζουν κατά τη διάρκεια ζωής τους". Η φράση αυτή, που έχει διατυπωθεί από πολλούς γνωστούς μηχανικούς λογισμικού, εμπεριέχει τη μεγαλύτερη ίσως αλήθεια της Τεχνολογίας Λογισμικού. Πώς μπορούμε να δημιουργούμε συστήματα τα οποία θα παραμένουν σταθερά ενόψει των επερχόμενων αλλαγών; Με άλλα λόγια, με ποια τεχνική μπορούμε αφενός να υλοποιούμε τις τρέχουσες απαιτήσεις με ευκολία και αφετέρου να προετοιμάζουμε το έδαφος για τις μελλοντικές απαιτήσεις; Πολύ συχνά, μία μοναδική αλλαγή σε ένα πρόγραμμα καταλήγει σε αλυσιδωτές αλλαγές (ripple effects) σε διάφορες εξαρτώμενες μονάδες, με αποτέλεσμα η σχεδίαση να χαρακτηρίζεται από ακαμψία και ευθραυστότητα. Το ζητούμενο είναι αν το σύστημα μπορεί να υποστεί αναδόμηση (refactoring), έτσι ώστε περαιτέρω αλλαγές του ιδίου τύπου να μην προκαλέσουν περαιτέρω τροποποιήσεις. O Bertrand Meyer το 1988 διατύπωσε προς αυτή την κατεύθυνση τη διάσημη αρχή της Ανοικτής-Κλειστής Σχεδίασης (Open-Closed Principle): Αρχή της Ανοικτής-Κλειστής Σχεδίασης: Οι οντότητες λογισμικού (κλάσεις, μονάδες, συναρτήσεις κλπ), θα πρέπει να είναι ανοικτές για επέκταση, αλλά κλειστές σε τροποποίηση. Η αρχή αυτή χαρακτηρίζει οποιοδήποτε "καλά σχεδιασμένο" σύστημα λογισμικού. Οι έννοιες "ανοικτή" και "κλειστή" σχεδίαση προσδιορίζουν ότι οι μονάδες λογισμικού (κλάσεις για αντικειμενοστρεφή συστήματα) που ικανοποιούν την αρχή, θα πρέπει να έχουν δύο κύριες ιδιότητες: "Ανοικτές για επέκταση". Η ιδιότητα αυτή σημαίνει ότι η συμπεριφορά της μονάδας μπορεί να επεκταθεί, προσθέτοντας νέες λειτουργίες που ικανοποιούν τις καινούργιες απαιτήσεις. "Κλειστές για τροποποίηση". Η επέκταση της συμπεριφοράς δεν οδηγεί σε αλλαγές του πηγαίου ή αντικείμενου κώδικα της μονάδας. Με άλλα λόγια, προστίθεται νέα λειτουργικότητα χωρίς να τροποποιηθεί η εκτελέσιμη εκδοχή της μονάδας, (βιβλιοθήκη, DLL, ή.jar αρχείο). Ουσιαστικά, αν η αρχή εφαρμοστεί ορθά, τότε η υλοποίηση περαιτέρω αλλαγών του ιδίου τύπου επιτυγχάνεται με την προσθήκη νέου κώδικα, όχι με την τροποποίηση υπάρχοντος κώδικα που ήδη λειτουργεί. Κάτι τέτοιο μπορεί να φαίνεται οξύμωρο: Ο λογικότερος τρόπος για να επεκταθεί η λειτουργικότητα μιας μονάδας είναι να τροποποιηθεί ο πηγαίος κώδικας της μονάδας. Κατά συνέπεια, το ερώτημα που προκύπτει είναι, πώς μπορούμε να αλλάξουμε το τι κάνει μια μονάδα, χωρίς να τροποποιήσουμε τη μονάδα; Ωστόσο, στην πράξη υπάρχουν σχετικά απλές και αποτελεσματικές στρατηγικές για την προσέγγιση αυτού του ιδανικού.

5 Αρχές σχεδίασης 101 Μια από τις θεμελιώδεις έννοιες σε όλες τις αντικειμενοστρεφείς γλώσσες προγραμματισμού, είναι η έννοια της αφαίρεσης (abstraction). Αφαίρεση ονομάζεται η σκόπιμη σύμπτυξη ή απόκρυψη πληροφορίας που αφορά μια διαδικασία ή ένα κατασκεύασμα, με σκοπό την καλύτερη κατανόηση άλλων απόψεων, λεπτομερειών ή δομής. Η εφαρμογή αφαίρεσης σε ένα σύστημα συμβάλλει στην απόκρυψη πληροφορίας (information hiding), μια τεχνική σύμφωνα με την οποία ορίζουμε σε κάποιο επίπεδο (π.χ. σε μια βασική κλάση) τις σημαντικές μόνο πλευρές μιας λειτουργίας ή μιας οντότητας και αγνοούμε τις υπόλοιπες λεπτομέρειες οι οποίες ορίζονται σε κάποιο χαμηλότερο επίπεδο (π.χ. σε μια παράγωγη κλάση). Η τεχνική της αφαίρεσης κατανοείται εύκολα εξετάζοντας έναν παγκόσμιο γεωγραφικό άτλαντα. Σε ένα πρώτο επίπεδο παρουσιάζεται μια απεικόνιση ολόκληρης της υφηλίου. Η απεικόνιση αυτή θα δείχνει τα σημαντικότερα στοιχεία, όπως τους ωκεανούς και τις ηπείρους. Η υπόλοιπη πληροφορία αποκρύπτεται σκοπίμως. Σε ένα χαμηλότερο επίπεδο, άλλοι χάρτες καλύπτουν μικρότερες σε έκταση γεωγραφικές περιοχές και περιλαμβάνουν περισσότερες λεπτομέρειες. Για παράδειγμα, ο χάρτης μιας ηπείρου θα δείχνει τα σύνορα των κρατών και ενδεχομένως ορισμένες μεγάλες πόλεις, ενώ δεν θα περιλαμβάνει λεπτομέρειες όπως ονόματα βουνών ή μικρότερων πόλεων που περιλαμβάνονται σε ακόμη ειδικότερους χάρτες (Σχήμα 5.2.1). Σχήμα 5.2.1: Τεχνική της αφαίρεσης σε ένα γεωγραφικό χάρτη Είναι προφανές ότι σε κάθε επίπεδο αυτού του παραδείγματος, ορισμένη ποσότητα πληροφορίας παρουσιάζεται ενώ συγκεκριμένες λεπτομέρειες αποκρύπτονται σκοπίμως, για λόγους εύκολης κατανόησης και διαχείρισης της συνολικής πληροφορίας. Η κάθοδος σε χαμηλότερο επίπεδο παρέχει μεγαλύτερη λεπτομέρεια. Η έννοια της αφαίρεσης μπορεί να θεωρηθεί ως η εισαγωγή δομής σε ένα σύστημα και μπορεί να υποδιαιρεθεί σε δύο υποκατηγορίες. Η πλέον συνήθης τεχνική αφαίρεσης είναι ο διαμερισμός ενός συνόλου στα τμήματα που το αποτελούν. Η τεχνική αυτή δεν διαφέρει από την κλασσική στρατηγική του "διαίρει και βασίλευε" (divide and conquer). Η άλλη τεχνική αφαίρεσης βασίζεται στην εξειδίκευση του γενικού, προσέγγιση που υλοποιείται στις αντικειμενοστρεφείς γλώσσες με τις αρχές της κληρονομικότητας. Οι τεχνικές του διαμερισμού σε τμήματα και του διαχωρισμού σε ειδικότερες κατηγορίες αντιστοιχούν στις δύο πιο σημαντικές μορφές αφαίρεσης στον αντικειμενοστρεφή προγραμματισμό, όπου είναι συνήθως γνωστές ως αφαίρεση τύπου "έχει" (has-a abstraction) και ως αφαίρεση τύπου "είναι" (is-a abstraction) αντίστοιχα. Κατά το διαμερισμό σε

102 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ τμήματα για παράδειγμα, ένα αυτοκίνητο "έχει" μία μηχανή και "έχει" ένα σύστημα μετάδοσης. Στο διαχωρισμό σε ειδικότερες κατηγορίες, ένα ποδήλατο "είναι" ένα όχημα, ή "είναι" ένα δίκυκλο για παράδειγμα. Οι δύο αυτές μορφές αφαίρεσης συνδέονται στενά με συγκεκριμένες προγραμματιστικές έννοιες σε διάφορες αντικειμενοστρεφείς γλώσσες προγραμματισμού. Στη C++, Java ή οποιαδήποτε άλλη αντικειμενοστρεφή γλώσσα προγραμματισμού, είναι δυνατόν να κατασκευαστούν αφαιρέσεις οι οποίες είναι μεν σταθερές και καθορισμένες, αναπαριστούν δε ένα απεριόριστο πλήθος δυνατών συμπεριφορών. Στο υπόλοιπο αυτού του βιβλίου, μια αφαίρεση θα αντιστοιχεί σε μια αφηρημένη (χωρίς υλοποίηση) βασική κλάση ή διασύνδεση στη Java και οι απεριόριστες συμπεριφορές αναπαρίστανται από όλες τις δυνατές κλάσεις απογόνους. Αυτό που είναι σημαντικό να κατανοηθεί είναι ότι μια μονάδα λογισμικού είναι δυνατό να "χειρίζεται" μια αφαίρεση. Μια τέτοια μονάδα μπορεί να είναι κλειστή για τροποποιήσεις καθώς εξαρτάται από την αφαίρεση που είναι σταθερή. Ωστόσο, η συμπεριφορά της μονάδας μπορεί να επεκταθεί δημιουργώντας νέες παράγωγες κλάσεις της αφαίρεσης. Στο Σχήμα 5.2.2 φαίνεται ένα απλό σχέδιο που δεν συμβαδίζει με την αρχή Ανοικτής- Κλειστής Σχεδίασης. Οι κλάσεις Πελάτης (Client) και Εξυπηρετητής (Server) είναι συγκεκριμένες, έχουν δηλαδή υλοποίηση και από αυτές μπορούν να δημιουργηθούν στιγμιότυπα. Η κλάση Πελάτης χρησιμοποιεί (uses) την κλάση Εξυπηρετητής για τη διεκπεραίωση κάποιας λειτουργίας. Αν ένα αντικείμενο της κλάσης Πελάτης θελήσει να χρησιμοποιήσει ένα εξυπηρετητή διαφορετικού τύπου, τότε η κλάση Πελάτης πρέπει να τροποποιηθεί, ώστε να ενημερώσει τη συσχέτιση μεταξύ των δύο κλάσεων. Πελάτης Εξυπηρετητής Σχήμα 5.2.2: O Πελάτης (Client) δεν είναι κλειστός σε τροποποιήσεις Το σχέδιο που συμμορφώνεται με την αρχή Ανοικτής-Κλειστής Σχεδίασης είναι αυτό που φαίνεται στο Σχήμα 5.2.3 και συνιστά το πρότυπο "Στρατηγική" που θα εξεταστεί σε επόμενο κεφάλαιο. H κλάση ΔιασύνδεσηΠελάτη είναι μια αφηρημένη κλάση με αφηρημένες μεθόδους (είτε μια διασύνδεση στη Java). Το όνομα ΔιασύνδεσηΠελάτη (και όχι ΔιασύνδεσηΕξυπηρετητή) επιλέγεται καθώς οι αφηρημένες κλάσεις σχετίζονται στενότερα με τις κλάσεις-πελάτες που τις χρησιμοποιούν, παρά με τις κλάσεις που τις υλοποιούν. Η κλάση Πελάτης χρησιμοποιεί αυτή την αφαίρεση, όμως κατά τη διάρκεια εκτέλεσης, τα αντικείμενα της κλάσης Πελάτης θα χρησιμοποιούν αντικείμενα της παράγωγης κλάσης Εξυπηρετητής. Αν τα αντικείμενα-πελάτες απαιτηθεί να χρησιμοποιήσουν μια διαφορετική κλάση εξυπηρετητή, τότε αρκεί η δημιουργία μιας νέας παράγωγης κλάσης της ΔιασύνδεσηΠελάτη και το "πέρασμα" της ως δείκτη (ή αναφοράς) στο αντικείμενο-πελάτη. Η κλάση Πελάτης θα παραμείνει αμετάβλητη.

5 Αρχές σχεδίασης 103 Πελάτης «interface» ΔιασύνδεσηΠελάτη Εξυπηρετητής Σχήμα 5.2.3: Πρότυπο "Στρατηγική" (Strategy Pattern): Ο Πελάτης είναι ανοικτός για επέκταση και κλειστός σε τροποποίηση Το παράδειγμα που ακολουθεί χρησιμοποιείται σε πολλά βιβλία Αντικειμενοστρεφούς Ανάλυσης και Σχεδίασης για να παρουσιάσει τον τρόπο με τον οποίο λειτουργεί ο πολυμορφισμός (π.χ. Meyer, 1997). Στη συνέχεια θα χρησιμοποιηθεί για να παρουσιαστεί μια περίπτωση παραβίασης και μια περίπτωση συμμόρφωσης με την αρχή της Ανοικτής- Κλειστής Σχεδίασης. Θεωρούμε μια εφαρμογή η οποία πρέπει να μπορεί να λαμβάνει ως είσοδο μια λίστα από κύκλους και τετράγωνα (που έχουν ήδη δημιουργηθεί), να διατρέξει τη λίστα και να σχεδιάζει τα αντίστοιχα σχήματα στην οθόνη. Παραβίαση της αρχής Ανοικτής-Κλειστής Σχεδίασης Η σχεδίαση του συστήματος που παραβιάζει την αρχή, φαίνεται στο διάγραμμα κλάσεων του Σχήματος 5.2.4. Η μέθοδος σχεδίασης της κλάσης Painter διατρέχει μια λίστα από αναφορές προς αντικείμενα-σχήματα, εξετάζει τον τύπο κάθε αντικειμένου κατά τη διάρκεια της εκτέλεσης και εκτελεί τις αντίστοιχες σχεδιαστικές ενέργειες, αντλώντας στοιχεία από τα αντίστοιχα αντικείμενα. Ο κώδικας Java για την εφαρμογή αυτή δίνεται στη συνέχεια. Κλάση - Πελάτης: Δημιουργεί διάφορα σχήματα και εν συνεχεία δημιουργεί ένα αντικείμενο Painter, στο οποίο περνά τα σχήματα ως παράμετρο Κλάση σχεδίασης σχημάτων. Παραβιάζει την αρχή Ανοικτής-Κλειστής Σχεδίασης Square ShapeApplication <<δημιουργεί>> Painter Circle <<δημιουργεί>> <<δημιουργεί>> Σχήμα 5.2.4: Εφαρμογή Σχεδίασης Σχημάτων: Παραβίαση της αρχής Ανοικτής-Κλειστής Σχεδίασης

104 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ import javax.swing.*; import java.awt.*; import java.util.arraylist; public class ShapeApplication extends JFrame{ private Painter canvas; public ShapeApplication() { Square S1 = new Square(50, 50, 50); Circle C1 = new Circle(100, 100, 40); Circle C2 = new Circle(200, 100, 20); ArrayList shapes = new ArrayList(); shapes.add(s1); shapes.add(c1); shapes.add(c2); canvas = new Painter(shapes); setcontentpane(canvas); setdefaultcloseoperation(jframe.exit_on_close); setsize(300, 300); setvisible(true); public static void main(string[] args) { ShapeApplication app1 = new ShapeApplication(); class Painter extends JPanel{ private ArrayList shapes; public Painter(ArrayList shapestodraw) { shapes = shapestodraw; public void paintcomponent(graphics g) { super.paintcomponent(g); for(int i=0; i<shapes.size(); i++) { if(shapes.get(i).getclass().getname() == "Square") { Square s = (Square)shapes.get(i); g.drawrect(s.gettopleftx(), s.gettoplefty(),

5 Αρχές σχεδίασης 105 s.getwidth(), s.getwidth()); else if(shapes.get(i).getclass().getname() == "Circle") { Circle c = (Circle)shapes.get(i); g.drawarc(c.getcenterx()-c.getradius(), c.getcentery()-c.getradius(), c.getradius()*2, c.getradius()*2, 0, 360); class Square { private int topleftx; private int toplefty; private int width; public Square(int x, int y, int itswidth) { topleftx = x; toplefty = y; width = itswidth; public int gettopleftx() { return topleftx; public int gettoplefty() { return toplefty; public int getwidth() { return width; class Circle { private int centerx; private int centery; private int radius; public Circle(int x, int y, int itsradius) { centerx = x; centery = y; radius = itsradius;

106 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ public int getcenterx() { return centerx; public int getcentery() { return centery; public int getradius() { return radius; Η κλάση Painter δεν συμμορφώνεται με την αρχή της Ανοικτής-Κλειστής Σχεδίασης, γιατί δεν μπορεί να "κλειστεί" έναντι νέων τύπων σχημάτων. Αν θέλαμε να επεκτείνουμε την κλάση ώστε να μπορεί να σχεδιάζει μια λίστα σχημάτων η οποία θα περιελάμβανε και τρίγωνα, θα έπρεπε να τροποποιήσουμε τον κώδικα της κλάσης. Στην πραγματικότητα, η κλάση πρέπει να τροποποιείται για κάθε νέο τύπο σχήματος που μπορεί να χρειαστεί να σχεδιαστεί στο μέλλον. Πέραν των άλλων σχεδιαστικών προβλημάτων, η κλάση Painter αναλαμβάνει επίσης αρμοδιότητες (την ίδια τη σχεδίαση του κάθε σχήματος), που κανονικά θα έπρεπε να κατανείμει μέσω διαβίβασης μηνυμάτων στα αντικείμενα κάθε σχήματος (delegation). Οι συνέπειες από την ανωτέρω επιλογή των σχεδιαστών του συστήματος θα ήταν πολύ πιο έντονες σε ένα πραγματικό σύστημα. Δομές όπως οι αλυσιδωτές εντολές if/else στη μέθοδο paintcomponent() της κλάσης Painter, θα εμφανίζονταν διαρκώς και σε διάφορα σημεία της εφαρμογής, η κάθε μία για διαφορετικούς σκοπούς. Η μια μέθοδος μπορεί να είχε ως σκοπό την μετακίνηση σχημάτων, η άλλη την αλλαγή των διαστάσεων ή χρωμάτων, διαγραφή τους κ.ο.κ. Η προσθήκη ενός νέου σχήματος θα επέβαλλε την αναζήτηση όλων των θέσεων όπου μπορεί να υπήρχαν αλυσιδωτές εντολές if/else ή εντολές switch. Πολλές από αυτές τις εντολές ελέγχου μπορεί να συνδυάζονταν απλοποιώντας τη λογική, εκτελώντας για παράδειγμα ορισμένες κοινές ενέργειες για κύκλους και τετράγωνα. Κατά συνέπεια, ο εντοπισμός, η κατανόηση και τροποποίηση όλων των σημείων κώδικα όπου πρέπει να προστεθεί το νέο σχήμα θα ήταν εξαιρετικά δύσκολες και δαπανηρές από πλευράς χρόνου, ενέργειες. Το σχέδιο κατά συνέπεια είναι εύθραυστο καθώς όλες αυτές οι αλλαγές ενδέχεται να εισάγουν σφάλματα. Αυτό που πρέπει επίσης να επισημανθεί, είναι ότι σε γλώσσες όπως η C++, όλες οι μονάδες λογισμικού που εξαρτώνται (μέσω οδηγιών include) από την κλάση Painter, θα έπρεπε να μεταγλωττιστούν εκ νέου σε περίπτωση αλλαγής της Painter. Τροποποίηση των αντικείμενων αρχείων μπορεί να σημαίνει εκ νέου εγκατάσταση βιβλιοθηκών και DLL σε ένα σύστημα. Η απλή ενέργεια της προσθήκης ενός μόνο νέου σχήματος στην εφαρμογή, προκαλεί μια αλυσίδα αλλαγών σε αρχεία πηγαίου κώδικα και σε πολύ περισσότερα αρχεία αντικείμενου κώδικα. Είναι εμφανές ότι η επίδραση της προσθήκης ενός μόνο νέου σχήματος είναι πολύ μεγάλη και μπορεί να προκαλέσει δυσκαμψία. Τέλος, η μονάδα λογισμικού Painter δεν είναι επαναχρησιμοποιήσιμη και το σύστημα εμφανίζει το σύμπτωμα της ακινησίας. Καθώς η μέθοδος paintcomponent() της κλάσης χρησιμοποιεί αναφορές προς τις συγκεκριμένες κλάσεις Square και Circle, σε κάθε νέο περιβάλλον όπου απαιτείται η επαναχρησιμοποίηση της κλάσης Painter, επιβάλλεται και η μεταφορά των αρχείων πηγαίου κώδικα ή των αντίστοιχων μεταγλωττισμένων αρχείων αυτών των κλάσεων.

5 Αρχές σχεδίασης 107 Συμμόρφωση με την Αρχή της Ανοικτής-Κλειστής Σχεδίασης Η βελτίωση του προγράμματος επιτυγχάνεται με εξαιρετικά κομψό τρόπο αξιοποιώντας τις δυνατότητες του πολυμορφισμού. Μια νέα αφηρημένη κλάση Shape προστίθεται στο σύστημα και ενσωματώνει οτιδήποτε μπορεί να μεταβληθεί, δηλαδή τη μέθοδο σχεδίασης οποιουδήποτε σχήματος. Η αφηρημένη κλάση ορίζει μια αφηρημένη μέθοδο draw(), ενώ οποιοδήποτε σχήμα είναι παράγωγη κλάση της Shape και υλοποιεί ανάλογα τη μέθοδο draw(). Το διάγραμμα κλάσεων παρουσιάζεται στο Σχήμα 5.2.5 και ο κώδικας σε Java στη συνέχεια. ShapeApplication <<δημιουργεί>> Painter Shape + draw() <<δημιουργεί>> <<δημιουργεί>> Circle +draw() Square +draw() Σχήμα 5.2.5: Εφαρμογή Σχεδίασης Σχημάτων: Συμμόρφωση με την αρχή Ανοικτής-Κλειστής Σχεδίασης import javax.swing.*; import java.awt.*; import java.util.arraylist; public class ShapeApplication extends JFrame{ private Painter canvas; public ShapeApplication() { Square S1 = new Square(50, 50, 50); Circle C1 = new Circle(100, 100, 40); Circle C2 = new Circle(200, 100, 20); ArrayList shapes = new ArrayList(); shapes.add(s1); shapes.add(c1); shapes.add(c2); canvas = new Painter(shapes); setcontentpane(canvas);

108 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ setdefaultcloseoperation(jframe.exit_on_close); setsize(300, 300); setvisible(true); public static void main(string[] args) { ShapeApplication app1 = new ShapeApplication(); class Painter extends JPanel{ private ArrayList shapes; public Painter(ArrayList shapestodraw) { shapes = shapestodraw; public void paintcomponent(graphics g) { super.paintcomponent(g); for(int i=0; i<shapes.size(); i++) { Shape shape = (Shape)shapes.get(i); shape.draw(g); abstract class Shape extends JComponent { abstract void draw(graphics g); class Square extends Shape { private int topleftx; private int toplefty; private int width; public Square(int x, int y, int itswidth) { topleftx = x; toplefty = y; width = itswidth; public void draw(graphics g) { g.drawrect(topleftx, toplefty, width, width);

5 Αρχές σχεδίασης 109 class Circle extends Shape { private int centerx; private int centery; private int radius; public Circle(int x, int y, int itsradius) { centerx = x; centery = y; radius = itsradius; public void draw(graphics g) { g.drawarc(centerx-radius, centery-radius, radius*2, radius*2, 0, 360); Η κλάση Painter είναι πλέον ανοικτή για αλλαγές και κλειστή σε τροποποιήσεις. Αν πλέον θελήσουμε να επεκτείνουμε τη συμπεριφορά της μεθόδου paintcomponent() έτσι ώστε να σχεδιάζουμε ένα νέο τύπο σχήματος, το μόνο που χρειάζεται είναι να προσθέσουμε μία νέα παράγωγη κλάση της κλάσης Shape. Η κλάση Painter δεν χρειάζεται να τροποποιηθεί και κατά συνέπεια συμμορφώνεται με την αρχή της Ανοικτής- Κλειστής Σχεδίασης. Το πρόγραμμα τροποποιείται προσθέτοντας νέο κώδικα και όχι αλλάζοντας κώδικα σε υπάρχουσες κλάσεις. Για την ακρίβεια, η προσθήκη ενός νέου σχήματος (π.χ. ενός Τριγώνου) δεν έχει απολύτως καμία επίδραση σε καμία από τις κλάσεις που φαίνονται παραπάνω, εκτός φυσικά από τον πελάτη της εφαρμογής που είναι υπεύθυνος για τη δημιουργία ενός τριγώνου στο σύστημα. Το σύμπτωμα της ευθραυστότητας εξαλείφθηκε αφού δεν απαιτείται ο εντοπισμός σημείων που χρήζουν αλλαγών. Επιπλέον δεν χρειάζεται να μεταγλωττιστούν άλλα αρχεία αντικείμενου κώδικα που είναι ήδη σε λειτουργία και το πρόγραμμα παύει να είναι δύσκαμπτο. Η μοναδική μονάδα που πρέπει να τροποποιηθεί και μεταγλωττιστεί είναι η μονάδα που δημιουργεί στιγμιότυπα της νέας παράγωγης κλάσης της Shape. Συνήθως αυτό γίνεται είτε στη συνάρτηση main(), είτε σε κάποια συνάρτηση που καλείται από τη main() ή σε κάποια μέθοδο αντικειμένου που δημιουργείται στη main() (τέτοια αντικείμενα είναι γνωστά ως εργοστάσια). Το τελευταίο σύμπτωμα που εξαλείφθηκε είναι η ακινησία. Η κλάση Painter μπορεί να επαναχρησιμοποιηθεί από οποιαδήποτε εφαρμογή απαιτεί τη σχεδίαση σχημάτων που περιλαμβάνονται σε μια λίστα χωρίς να πρέπει να μεταφερθούν και οι κλάσεις Circle ή Square. Η τεχνική που χρησιμοποιήθηκε στο συγκεκριμένο παράδειγμα υλοποιεί το πρότυπο "Στρατηγική" που παρουσιάστηκε διαγραμματικά στο Σχήμα 5.2.3. Στο Σχήμα 5.2.6 παρουσιάζεται μια εναλλακτική δομή που αντιστοιχεί στο πρότυπο σχεδίασης "Μέθοδος

110 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ Υπόδειγμα". Η αφηρημένη βασική κλάση Στρατηγική έχει ένα σύνολο συγκεκριμένων δημόσιων μεθόδων, οι οποίες υλοποιούν μια στρατηγική κάποιου τύπου, αντίστοιχα με τις μεθόδους των πελατών στην προηγούμενη περίπτωση. Όπως και πριν, αυτές οι μέθοδοι περιγράφουν κάποια εργασία που πρέπει να γίνει υπό όρους ορισμένων αφηρημένων διασυνδέσεων. Ωστόσο, σε αυτή την περίπτωση, η αφηρημένη διασύνδεση είναι μέρος της ίδιας της κλάσης Στρατηγική. Στη C++, η αφαίρεση θα επρόκειτο για αμιγώς υπερβατές μεθόδους, ενώ στη Java θα ήταν abstract μέθοδοι. Αυτές οι αφηρημένες μέθοδοι υλοποιούνται στις παράγωγες κλάσεις της κλάσης Στρατηγική. Επομένως, η συμπεριφορά που προσδιορίζεται μέσα στην κλάση Στρατηγική μπορεί να επεκταθεί δημιουργώντας νέες υποκλάσεις της, χωρίς να απαιτείται τροποποίηση του πηγαίου κώδικα της κλάσης Στρατηγική και φυσικά ούτε των κλάσεων που τη χρησιμοποιούν, προστατεύοντας το υπόλοιπο σύστημα από τη "διάδοση" των αλλαγών. Στην εφαρμογή της σχεδίασης σχημάτων, το πρότυπο "Μέθοδος Υπόδειγμα" θα μπορούσε να υλοποιηθεί με την μεταφορά των λειτουργιών σάρωσης των σχημάτων της λίστας μέσα στην κλάση Shape, η οποία θα διατηρούσε την αφηρημένη μέθοδο σχεδίασης draw() (και θα μπορούσε πλέον να δηλώνεται ως ιδιωτική). Στρατηγική +ΜέθοδοςΣτρατηγικής() -ΜέθοδοςΥλοποίησης() Υλοποίηση -ΜέθοδοςΥλοποίησης() Σχήμα 5.2.6: Πρότυπο "Μέθοδος Υπόδειγμα" (Template Method Pattern): Η βασική κλάση είναι ανοικτή για επέκταση και κλειστή σε τροποποίηση Συμπερασματικά Ο αντικειμενοστρεφής προγραμματισμός προσφέρει σημαντικά πλεονεκτήματα στην περίπτωση ανάπτυξης συστημάτων που πρόκειται να εξελιχθούν λόγω αλλαγών στις απαιτήσεις. Αν υποθέσουμε ότι ένα σύστημα πρόκειται να κατασκευασθεί και να παραμείνει ως έχει για ολόκληρο τον κύκλο ζωής του, τότε ο αντικειμενοστρεφής όχι μόνο δεν υπερέχει έναντι άλλων μοντέλων, αλλά ίσως να προσθέτει περιττή πολυπλοκότητα. Αν όμως, όπως συμβαίνει στην πραγματικότητα, οι αλλαγές στις απαιτήσεις επιβάλλουν την τροποποίηση του λογισμικού, τότε η αρχή της Ανοικτής-Κλειστής Σχεδίασης εξασφαλίζει ότι οι αλλαγές θα πραγματοποιηθούν με την μικρότερη δυνατή προσπάθεια, χρόνο και κόστος.

5 Αρχές σχεδίασης 111 5.3 Αρχή της Ενσωμάτωσης Η αρχή της ενσωμάτωσης ή αρχή της ενθυλάκωσης (Encapsulation Principle) αποτελεί έναν από τους θεμελιώδεις κανόνες αντικειμενοστρεφούς προγραμματισμού και από τα πρώτα στοιχεία που μαθαίνει κάποιος να εφαρμόζει στις αντικειμενοστρεφείς γλώσσες, ανεξαρτήτως του αν δεν συνειδητοποιεί πάντοτε τη χρησιμότητά του. Η αρχή διατυπώνεται ως εξής: Αρχή της Ενσωμάτωσης: Η εσωτερική κατάσταση ενός αντικειμένου πρέπει να είναι τροποποιήσιμη μόνο μέσω της δημόσιας διασύνδεσης του. Με τον ορισμό μιας κλάσης επιτυγχάνεται ομαδοποίηση της συμπεριφοράς και της κατάστασης: Κάθε αντικείμενο στιγμιότυπο μιας κλάσης περιλαμβάνει μέλη δεδομένων (ιδιότητες) που συνιστούν την εσωτερική κατάσταση του αντικειμένου και μεθόδους (που δηλώνονται σε επίπεδο κλάσης) και καθορίζουν τη συμπεριφορά μιας οικογένειας αντικειμένων. Σύμφωνα με την ανωτέρω αρχή, οι ιδιότητες πρέπει να μην είναι προσπελάσιμες εκτός της κλάσης, δηλαδή η τροποποίηση των τιμών των ιδιοτήτων θα πρέπει να επιτρέπεται μόνο μέσω της πρόσβασης που παρέχουν οι δημόσια διαθέσιμες μέθοδοι (Σχήμα 5.3.1). κατάσταση συμπεριφορά { { Αντικείμενο1 : Λογαριασμός - όνομα_κατόχου - αριθμός - υποκατάστημα_τράπεζας - υπόλοιπο + υπολογισμός_τόκων( ) + εκτύπωση_υπολοίπου( ) + κατάθεση( ) + ανάληψη( ) πρόσβαση στις ιδιότητες μόνο μέσω των δημόσιων μεθόδων Σχήμα 5.3.1: Αρχή της Ενσωμάτωσης σε ένα αντικείμενο τύπου Λογαριασμός Κατ' αυτόν τον τρόπο, ο σχεδιαστής της κλάσης, επιβάλλοντας στον "έξω κόσμο" να προσπελάσει τις ιδιότητες μέσω των δημόσια διαθέσιμων μεθόδων, έχει τη δυνατότητα να καθορίσει τους κανόνες και τα δικαιώματα πρόσβασης. Πρακτικά, η αρχή της ενσωμάτωσης υλοποιείται απλά, θέτοντας την ορατότητα όλων των ιδιοτήτων ως ιδιωτική (private). Τα ιδιωτικά μέλη δεδομένων αποκρύπτονται κατά συνέπεια από τους προγραμματιστές άλλων κλάσεων ή άλλων συστημάτων (παραμένει ωστόσο η δυνατότητα προσπέλασής τους από άλλα αντικείμενα της ίδιας κλάσης). Το πλεονέκτημα που προκύπτει από την εφαρμογή της αρχής της ενσωμάτωσης σχετίζεται με τη δυνατότητα διατήρησης της εγκυρότητας και συνέπειας της κατάστασης ενός αντικειμένου. Για την καλύτερη κατανόηση της έννοιας της εγκυρότητας ενός αντικειμέ-

112 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ νου θεωρούμε μια κλάση TimeStamp που αφορά χρονικά στιγμιότυπα, τα οποία περιλαμβάνουν την ώρα, λεπτά και δευτερόλεπτα μιας δεδομένης χρονικής στιγμής: class TimeStamp { public: TimeStamp(); TimeStamp(int hr, int min, int sec); void printtimestamp(); ; int hour; //μέλη δεδομένων με δημόσια ορατότητα!!! int minute; int second; Στην ανωτέρω κλάση οι προγραμματιστές έχουν αφήσει περιθώρια για την εμφάνιση σφαλμάτων θέτοντας την ορατότητα των μελών δεδομένων ως δημόσια (public). Έστω ένα συγκεκριμένο αντικείμενο αυτής της κλάσης όπως δημιουργείται από την κλήση του κατασκευαστή: TimeStamp T1(23, 45, 17); Αν κάποια κλάση πελάτης της κλάσης TimeStamp θελήσει να αυξήσει κατά δύο ώρες την χρονική στιγμή επιχειρώντας να τροποποιήσει απευθείας την ιδιότητα hour του αντικείμενου T1 ως εξής: //κώδικας κλάσης πελάτη... Τ1.hour++; T1.hour++; θα προκύψει ένα αντικείμενο (το Τ1), του οποίου η κατάσταση δεν θα είναι έγκυρη καθώς η χρονική στιγμή στην οποία θα αναφέρεται θα είναι 25:45:17 (!!). Με άλλα λόγια, η ορθότητα του αντικειμένου θα έχει πάψει να ισχύει, καθώς μια από τις αναλλοίωτες ενός αντικειμένου TimeStamp δεν θα είναι πλέον αληθής (όπου αναλλοίωτη είναι μια λογική πρόταση που πρέπει να είναι πάντοτε αληθής). Αν από την άλλη, η πρόσβαση στα μέλη δεδομένων επιτυγχάνεται μόνο μέσω των δημόσια διαθέσιμων μεθόδων, τότε ο σχεδιαστής της κλάσης μπορεί να εγγυηθεί την εγκυρότητα των αντικειμένων σε όλες τις χρονικές στιγμές. Σχεδιάζοντας τον κατασκευαστή ώστε να παράγει μόνο έγκυρα αντικείμενα της κλάσης TimeStamp (τα οποία για παράδειγμα έχουν τιμές ιδιοτήτων που πληρούν προκαθορισμένες συνθήκες π.χ. 0 hour 23 ) και τις μεθόδους, ώστε να μην καταργούν την ισχύ των αναλλοίωτων, ε- ξασφαλίζεται ότι ο κώδικας καμιας άλλης κλάσης δεν μπορεί να αλλοιώσει την ορθότητα ενός αντικειμένου. Στο ανωτέρω παράδειγμα, η δυνατότητα αύξησης της ώρας κατά ένα, μπορεί και πρέπει να παρέχεται μόνο μέσω κατάλληλης μεθόδου incrementhour(), η οποία θα ελέγχει την περίπτωση υπέρβασης των ορίων. Ταυτοχρόνως, όλα τα δεδομένα πρέπει να είναι ιδιωτικά. Για παράδειγμα:

5 Αρχές σχεδίασης 113 class TimeStamp {... public: void incrementhour(); private: int hour; int minute; int second;... ; void TimeStamp::incrementHour() { hour++; if(hour == 24) hour = 0; Συμπερασματικά Η παραβίαση της αρχής της ενσωμάτωσης είναι δυνατόν να προκαλέσει ορισμένα από τα πιο σημαντικά προβλήματα συντήρησης αντικειμενοστρεφούς λογισμικού. Επιτρέποντας την τροποποίηση ιδιοτήτων ενός αντικειμένου μέσα από μεθόδους άλλων κλάσεων, στην ουσία καταργείται η ομαδοποίηση δεδομένων και συμπεριφοράς, στοιχείο που είναι υπεύθυνο για τα περισσότερα πλεονεκτήματα του αντικειμενοστρεφούς μοντέλου. Αν τα δεδομένα είναι δημόσια, είναι πολύ δύσκολο να καθοριστεί ποιο τμήμα του κώδικα του συστήματος εξαρτάται από αυτά τα δεδομένα χρησιμοποιώντας τα. Το πρόβλημα αυτό, είναι ακριβώς ένα από τα μειονεκτήματα των διαδικασιακών γλωσσών. Αν λόγω μελλοντικών απαιτήσεων αλλάξει η αναπαράσταση των δεδομένων, τότε όλα τα τμήματα κώδικα που τα χρησιμοποιούν πρέπει να ενημερωθούν, ενώ ο εντοπισμός αυτών των τμημάτων είναι εξαιρετικά επίπονη διαδικασία. Το λογισμικό αποκτά επομένως δυσκαμψία, ευθραυστότητα και ακινησία. Αν όμως ισχύει η αρχή της ενσωμάτωσης, τότε σε περίπτωση αλλαγών στα δεδομένα, απαιτείται η ενημέρωση του κώδικα μόνο της κλάσης που περιέχει και προσπελαύνει τα δεδομένα, ενώ οι κλάσεις-πελάτες που χρησιμοποιούν τα δεδομένα μέσω της δημόσιας διασύνδεσης, παραμένουν αναλλοίωτες. 5.4 Αρχή της Χαμηλής Σύζευξης Στο στάδιο της αρχιτεκτονικής σχεδίασης κάθε συστήματος λογισμικού στόχος είναι η αποσύνθεση σε μονάδες (modules) που αναλαμβάνουν την υλοποίηση τμημάτων της συνολικής λειτουργικότητας. Μια μονάδα είναι ένα ανεξάρτητο συστατικό λογισμικού (το οποίο μπορεί συνήθως να μεταγλωττίζεται ανεξάρτητα) με σαφώς καθορισμένη λειτουργικότητα και σαφώς καθορισμένο σύνολο εισόδων και εξόδων. Η έννοια της μονάδας διαφέρει ανάλογα με το μοντέλο και τη γλώσσα προγραμματισμού, ενώ στα αντικειμενοστρεφή συστήματα ως μονάδες θεωρούνται οι κλάσεις. Η σχεδίαση συνίσταται στον

114 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ προσδιορισμό του συνόλου των συστατικών και των μεταξύ τους διασυνδέσεων, ώστε να ικανοποιείται ένα συγκεκριμένο σύνολο απαιτήσεων. Ωστόσο, οι τρόποι με τους οποίους οι απαιτήσεις μπορούν να μεταφραστούν σε αρχιτεκτονικά σχέδια είναι πάρα πολλοί, χωρίς να είναι εύκολη η διάκριση μεταξύ καλής και κακής σχεδίασης και χωρίς να υπάρχουν αυστηροί κανόνες. Υπάρχουν όμως ορισμένα επιθυμητά χαρακτηριστικά για ένα σχέδιο καλής ποιότητας, με βάση τα οποία μπορεί να καθοδηγηθεί η διαδικασία σχεδίασης. Για παράδειγμα, βασικός στόχος της σχεδίασης είναι να καταστήσει όσο το δυνατόν πιο ανεξάρτητα τα συστατικά λογισμικού. Δύο έννοιες πολύ μεγάλης σημασίας για την Τεχνολογία Λογισμικού που σχετίζονται με το βαθμό α- νεξαρτησίας των συστατικών, είναι η σύζευξη και η συνεκτικότητα. Η σύζευξη (coupling) αναφέρεται στο βαθμό εξάρτησης μεταξύ δύο συστατικών λογισμικού. Η εξάρτηση μπορεί να οφείλεται σε πολλούς λόγους, όπως η κλήση μεθόδων (συναρτήσεων) του ενός συστατικού από το άλλο, το "πέρασμα" δεδομένων από το ένα συστατικό στο άλλο ή η χρήση συστατικών ενός τύπου ως ιδιότητες (μεταβλητές) σε κάποιο άλλο. Δύο μονάδες λογισμικού που εξαρτώνται σε μεγάλο βαθμό μεταξύ τους λέγεται ότι έχουν ισχυρή σύζευξη, ενώ αν η εξάρτηση είναι ασθενής πρόκειται για χαλαρή σύζευξη. Δύο μονάδες μπορεί να έχουν μηδενική σύζευξη εάν δεν υπάρχει καμία συσχέτιση μεταξύ τους. Η συνεκτικότητα (cohesion) αναφέρεται στο βαθμό "εσωτερικής" λειτουργικής συνάφειας μεταξύ των τμημάτων ενός συστατικού. Όσο πιο συνεκτικό είναι ένα συστατικό, τόσο πιο πολύ τα επιμέρους τμήματά του σχετίζονται μεταξύ τους και συνεργάζονται για την επίτευξη ενός κοινού σκοπού. Αν για παράδειγμα σε μια μονάδα λογισμικού, έχουν τοποθετηθεί άσχετες λειτουργίες ή δεδομένα (π.χ. μια λειτουργία υπολογισμού τόκων και μια λειτουργία κρυπτογράφησης κειμένου), τότε η συνεκτικότητα είναι χαμηλή (συμπτωματική συνεκτικότητα). Στην ιδανική περίπτωση, μια μονάδα περιλαμβάνει πλήρως συσχετισμένα τμήματα που όλα είναι απαραίτητα για την εκτέλεση της ίδιας μοναδικής λειτουργίας (λειτουργική συνεκτικότητα). Η συνεκτικότητα θα εξεταστεί στα πλαίσια μιας άλλης αρχής που έχει ως στόχο την αύξηση της, την αρχή της Μοναδικής Αρμοδιότητας. Η σύζευξη και η συνεκτικότητα μπορούν να ποσοτικοποιηθούν και να μετρηθούν τόσο στον διαδικασιακό όσο και στον αντικειμενοστρεφή προγραμματισμό. Σε οποιοδήποτε σύστημα λογισμικού, κύριο μέλημα της διαδικασίας σχεδίασης είναι η ικανοποίηση της ακόλουθης αρχής: Αρχή της Χαμηλής Σύζευξης: Σε ένα σχέδιο λογισμικού πρέπει να επιδιώκεται η επίτευξη της μικρότερης δυνατής σύζευξης μεταξύ των συστατικών του. Η επιδίωξη χαμηλής σύζευξης, κυρίως αξιοποιώντας τις τεχνικές της αφαίρεσης και της απόκρυψης πληροφορίας, εξασφαλίζει ανεξαρτησία μεταξύ των συστατικών του σχεδίου. Με αυτό τον τρόπο γίνεται ευκολότερη η υλοποίηση, ο έλεγχος και η συντήρηση του λογισμικού. Αν δύο ή περισσότερες μονάδες είναι σχετικά ανεξάρτητες, η κατανομή του έργου ανάπτυξης κάθε μονάδας σε διαφορετικούς προγραμματιστές είναι απλούστερη, καθώς δεν απαιτείται συχνή αλληλεπίδραση για την κατανόηση τους. Επιπρόσθετα, η διόρθωση του λογισμικού κατά τη διάρκεια του ελέγχου απλοποιείται, καθώς είναι ευκολότερος ο εντοπισμός των σφαλμάτων εντός των ορίων κάθε μονάδας. Τέλος, κατά την τρο-

5 Αρχές σχεδίασης 115 ποποίηση απαιτήσεων διευκολύνεται ο καθορισμός των μονάδων που πρέπει να προσαρμοστούν και φυσικά όσο πιο ανεξάρτητες είναι οι μονάδες, τόσο απλούστερη είναι η διόρθωση και μεταβολή του κώδικα. Για να γίνουν κατανοητά τα ανωτέρω, θεωρούμε ως παράδειγμα κακής σχεδίασης, το αντικειμενοστρεφές μοντέλο ενός φούρνου μικροκυμάτων. Κάθε τμήμα του φούρνου μοντελοποιείται ως ξεχωριστή μονάδα (κλάση). Το διάγραμμα κλάσεων της UML παρουσιάζεται στο επόμενο σχήμα: ΠλήκτροΑκύρωσης ΣωλήναςΜικροκυμάτων Κουδούνι Χρονόμετρο Πόρτα ΠλήκτροΛειτουργίας Λαμπτήρας Σχήμα 5.4.1: Αντικειμενοστρεφές μοντέλο φούρνου μικροκυμάτων με υψηλή σύζευξη Η υψηλή σύζευξη στο ανωτέρω σχέδιο είναι προφανής, καθώς σχεδόν κάθε κλάση διατηρεί συσχετίσεις προς πολλές άλλες, ώστε να μπορεί να αποστέλλει μηνύματα σε αυτές. Η λειτουργικότητα του συστήματος έχει διαμοιραστεί ορθά μεταξύ των κλάσεων, ωστόσο η υψηλή σύζευξη μεταξύ τους δυσχεραίνει τη συντήρηση και την επαναχρησιμοποίηση κάθε μονάδας. Θεωρούμε για παράδειγμα μια νέα απαίτηση, η οποία επιτρέπει στο χρονόμετρο να σταματήσει και να συνεχίσει από το σημείο που διεκόπη η λειτουργία του φούρνου. Ακόμα και αν η τροποποίηση του κώδικα της κλάσης Χρονόμετρο είναι απλή, η υψηλή σύζευξη θα επιβάλλει αλλαγές στις συσχετιζόμενες κλάσεις ΠλήκτροΑκύρωσης, ΠλήκτροΛειτουργίας και Πόρτα. Σε κάθε περίπτωση, οι συσχετιζόμενες κλάσεις πρέπει να ελεγχθούν εκ νέου για την ορθή συμπεριφορά τους και σε ορισμένες γλώσσες προγραμματισμού να επαναμεταγλωττιστούν. Επιπλέον, η κλάση Χρονόμετρο, ενδεχομένως να αποτελεί κατάλληλη κλάση για επαναχρησιμοποίηση σε διαφορετικές εφαρμογές όπου απαιτείται μέτρηση του χρόνου. Ωστόσο, σε οποιοδήποτε νέο σύστημα χρησιμοποιηθεί θα πρέπει να "μεταφερθούν" και οι αναφορές προς τις κλάσεις Κουδούνι, ΣωλήναςΜικροκυμάτων και Λαμπτήρας και στην περίπτωση που δεν απαιτούνται να

116 ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΣΧΕΔΙΑΣΗ λάβουν την τιμή null, ελέγχοντας αν δημιουργούνται προβλήματα στις σχετικές μεθόδους της κλάσης. Η μείωση της σύζευξης δεν επιτυγχάνεται με την απλή εφαρμογή κάποιας τεχνικής. Ο περιορισμός του βαθμού σύζευξης πρέπει να αποτελεί προτεραιότητα των σχεδιαστών καθ'όλα τα στάδια ανάπτυξης ενός έργου λογισμικού. Πολλά από τα πρότυπα σχεδίασης που θα αναφερθούν στο επόμενο κεφάλαιο έχουν ως στόχο την αποσύνδεση τμημάτων λογισμικού, ωστόσο δεν αποτελούν πανάκεια και βεβαίως δεν μπορούν να βελτιώσουν σημαντικά ένα σχέδιο όπου οι περισσότερες μονάδες έχουν εξαρτήσεις μεταξύ τους. Είναι όμως εύκολο να μετράται ο βαθμός σύζευξης σε ένα σύστημα, καθώς έχουν προταθεί πολλές μετρικές για την ποσοτικοποίησή της και πολλές από αυτές τις μετρικές έχουν ενσωματωθεί σε λογισμικό υποστήριξης της διαδικασίας ανάπτυξης. Για παράδειγμα, η μετρική "Σύζευξη μεταξύ Αντικειμένων" ή μετρική CBO (Coupling Between Objects) που προτάθηκε από τους Chidamber και Kemerer (Chidamber, 1994), ποσοτικοποιεί το βαθμό σύζευξης μεταξύ κλάσεων. Η τιμή της μετρικής για μια κλάση C ισούται με τον αριθμό άλλων κλάσεων με τις οποίες υπάρχει σύζευξη (εξαιρώντας σύζευξη λόγω κληρονομικότητας). Ως σύζευξη μεταξύ δύο κλάσεων νοείται η χρήση μεθόδων της μιας κλάσης από την άλλη ή η απευθείας πρόσβαση σε μέλη δεδομένων. Στη βιβλιογραφία έχει προταθεί πληθώρα άλλων μετρικών σύζευξης, που λαμβάνουν όλους τους τύπους εξάρτησης μεταξύ κλάσεων, όπως η χρήση τύπων μιας κλάσης ως παραμέτρους σε μεθόδους της άλλης κλάσης, η δημιουργία αντικειμένων, ύπαρξη φιλικών κλάσεων, αποστολή μηνυμάτων κατά τη διάρκεια εκτέλεσης κλπ. Αυτό που είναι σημαντικό ωστόσο, είναι από τη στιγμή καθορισμού της έννοιας της σύζευξης με οποιονδήποτε τρόπο, να α- ποτελεί διαρκή στόχο η μείωση της σε όλες τις φάσεις ανάπτυξης. Συμπερασματικά Δύο μονάδες που εξαρτώνται η μία από την άλλη υλοποιούνται, ελέγχονται και συντηρούνται δυσκολότερα, καθώς για την κατανόηση της μιας κλάσης απαιτείται και η πλήρης κατανόηση της άλλης. Η έννοια της σύζευξης αναφέρεται στις σχέσεις εξάρτησης μεταξύ κλάσεων και για το λόγο αυτό, σε κάθε σύστημα λογισμικού, η σύζευξη μεταξύ συστατικών πρέπει να είναι όσο το δυνατόν μικρότερη. 5.5 Αρχή της Μοναδικής Αρμοδιότητας Η αρχή της Μοναδικής Αρμοδιότητας (Single-Responsibility Principle) ουσιαστικά εξασφαλίζει υψηλή συνεκτικότητα για τα συστατικά ενός συστήματος λογισμικού. Περιγράφηκε αρχικά στην εργασία του Tom DeMarco (1979) και διατυπώνεται ως εξής: Αρχή της Μοναδικής Αρμοδιότητας: Μια κλάση πρέπει να έχει μόνο ένα λόγο να αλλάξει. Μια κλάση δεν πρέπει να έχει περισσότερες από μία αρμοδιότητες. Κάθε αρμοδιότητα συνιστά έναν εν δυνάμει άξονα αλλαγών (axis of change) όπως αναφέρθηκε στο παράδειγμα της Εισαγωγής. Όταν αλλάξουν οι απαιτήσεις του συστήματος, οι αλλαγές εμφανί-