Cohesion (συνοχή) είναι μια μετρική που δείχνει πόσο στενά συνδεδεμένα είναι τα στοιχεία μέσα σε μια ενότητα ή κλάση. Σύμφωνα με το Wikipedia, η υψηλή συνοχή αποτελεί ένδειξη μιας καλά σχεδιασμένης ενότητας, όπου όλες οι μέθοδοι και τα πεδία εργάζονται για έναν σκοπό. Cohesion επηρεάζει άμεσα τη συντηρησιμότητα του κώδικα και αντιπαραβάλλεται με το coupling — τη συζευκτικότητα μεταξύ ενοτήτων.
Βασικά σημεία
Cohesion (συνοχή) — μια μετρική που αξιολογεί πόσο λογικά συνδέονται μεταξύ τους οι μέθοδοι, τα πεδία και οι ιδιότητες μέσα σε μια κλάση ή ενότητα. Μια ενότητα με υψηλή συνοχή εκτελεί μία εργασία και περιέχει μόνο τα στοιχεία που είναι απαραίτητα για την εκτέλεσή της. Μια ενότητα με χαμηλή συνοχή προσπαθεί να κάνει πολλά πράγματα ταυτόχρονα — οι μέθοδοι είναι ασθενώς συνδεδεμένες ως προς το νόημα.
Στο πλαίσιο του αντικειμενοστρεφούς προγραμματισμού, η συνοχή συνδέεται στενά με την Αρχή της Μοναδικής Ευθύνης (Single Responsibility Principle — S). Αν μια κλάση έχει μία σαφή ευθύνη, η συνοχή της είναι συνήθως υψηλή. Αν η κλάση ασχολείται και με UI, και με επιχειρησιακή λογική, και με δικτύωση — η συνοχή είναι χαμηλή, και αξίζει να χωριστεί σε πολλές ξεχωριστές κλάσεις με στενότερη ευθύνη.
Η κατανόηση του cohesion βοηθά τον προγραμματιστή να αποφασίζει για αναδιάρθρωση. Όταν βλέπετε ότι σε μια κλάση υπάρχει μια μέθοδος που δεν χρησιμοποιεί τα πεδία της κλάσης, αυτό είναι σήμα χαμηλής συνοχής. Μια τέτοια μέθοδος είτε περισσεύει στην κλάση, είτε η κλάση είναι λανθασμένα σχεδιασμένη. Η επιδίωξη υψηλής συνοχής είναι μια συνεχής εργασία βελτίωσης της αρχιτεκτονικής σε κάθε επίπεδο κώδικα.
Στη μηχανική λογισμικού διακρίνουμε επτά επίπεδα συνοχής, από το χειρότερο στο καλύτερο. Η κατανόηση αυτής της κλίμακας επιτρέπει την αντικειμενική αξιολόγηση της ποιότητας μιας ενότητας και την κατανόηση της κατεύθυνσης κατά την αναδιάρθρωση. Όσο υψηλότερο το επίπεδο, τόσο πιο συντηρήσιμος και κατανοητός θα είναι ο κώδικας.
Τυχαία (coincidental) — το χειρότερο επίπεδο, όταν τα στοιχεία σε μια ενότητα ομαδοποιούνται τυχαία, χωρίς καμία λογική σύνδεση. Παράδειγμα: η κλάση Utilities, όπου συγκεντρώνονται μέθοδοι μορφοποίησης ημερομηνίας, αποστολής email και υπολογισμού έκπτωσης. Μια τέτοια κλάση είναι αδύνατο να κατανοηθεί χωρίς ανάγνωση όλων των μεθόδων, και η αλλαγή μιας μεθόδου μπορεί να σπάσει άλλες μόνο και μόνο επειδή βρίσκονται μαζί.
Λογική (logical) συνοχή — τα στοιχεία εκτελούν λογικά σχετικές αλλά διαφορετικές ως προς την ουσία εργασίες. Μια κλάση με μεθόδους parseJSON, parseXML και parseCSV είναι λογικά συνδεδεμένη με το θέμα «ανάλυση», αλλά κάθε μέθοδος κάνει εντελώς διαφορετική δουλειά. Πρόβλημα: όταν προστίθεται μια νέα μορφή (YAML), η κλάση μεγαλώνει και η διεπαφή της διογκώνεται.
Χρονική (temporal) συνοχή — τα στοιχεία ομαδοποιούνται με βάση τον χρόνο εκτέλεσης. Η κλάση AppInitializer, που ρυθμίζει τη βάση δεδομένων, φορτώνει τη διαμόρφωση, αρχικοποιεί τα analytics — όλα αυτά συμβαίνουν κατά την εκκίνηση της εφαρμογής, αλλά οι ίδιες οι εργασίες δεν σχετίζονται μεταξύ τους. Καλύτερα να χωριστούν σε ξεχωριστά Initializer για κάθε τομέα ευθύνης.
Διαδικαστική (procedural) συνοχή προκύπτει όταν τα στοιχεία ενώνονται με βάση τη σειρά εκτέλεσης. Η ενότητα «Επεξεργασία παραγγελίας» περιέχει μεθόδους validateCart, processPayment, sendConfirmation — κάθε μέθοδος καλείται αυστηρά μετά την προηγούμενη. Αυτό είναι καλύτερο από την τυχαία ή λογική συνοχή, αλλά εξακολουθεί να μην είναι ιδανικό: κάθε βήμα μπορεί να μεταφερθεί σε ξεχωριστή ενότητα.
Επικοινωνιακή (communicational) συνοχή — τα στοιχεία εργάζονται με τα ίδια δεδομένα. Η κλάση UserService με μεθόδους getUser, updateUser, deleteUser ενώνεται από την κοινή οντότητα User. Αυτό είναι σημαντικά καλύτερο από τη διαδικαστική συνοχή: η κλάση έχει σαφή θεματική περιοχή. Οι περισσότερες κλάσεις Repository σε έργα για κινητά έχουν επικοινωνιακή συνοχή.
Λειτουργική (functional) συνοχή — το υψηλότερο επίπεδο, όταν κάθε στοιχείο της ενότητας συμμετέχει στην εκτέλεση μιας εργασίας. Η κλάση PasswordValidator με μία μόνο μέθοδο validate, που ελέγχει το μήκος, την ύπαρξη χαρακτήρων και την πολυπλοκότητα του κωδικού — παράδειγμα λειτουργικής συνοχής. Αν μια τέτοια κλάση αλλάξει, τότε μόνο επειδή άλλαξαν οι κανόνες επικύρωσης κωδικών.
Η επίτευξη λειτουργικής συνοχής είναι ο κύριος στόχος της αρχιτεκτονικής αναδιάρθρωσης. Κάθε κλάση πρέπει να έχει ακριβώς έναν λόγο αλλαγής. Στην ανάπτυξη για κινητά, η λειτουργική συνοχή επιτυγχάνεται μέσω της εξαγωγής ξεχωριστών Use Cases, προσαρμοσμένων View, μορφοποιητών και επικυρωτών. Κάθε τέτοια κλάση είναι ένα ολοκληρωμένο δομικό στοιχείο με σαφή τομέα ευθύνης.
Cohesion και coupling — δύο όψεις της ίδιας ποιότητας. Όσο υψηλότερη είναι η συνοχή μέσα σε μια ενότητα, τόσο χαμηλότερη είναι συνήθως η συζευκτικότητα μεταξύ ενοτήτων. Ένα καλά σχεδιασμένο σύστημα επιδιώκει ταυτόχρονα υψηλή συνοχή εσωτερικά και χαλαρή σύνδεση εξωτερικά. Αυτός ο κανόνας θεωρείται θεμελιώδης στη μηχανική λογισμικού από τη δεκαετία του 1970.
Η σχέση cohesion-coupling μπορεί να παρουσιαστεί ως ισορροπία. Αν ο προγραμματιστής θυσιάζει τη συνοχή, συνδυάζοντας πολλές εργασίες σε μία κλάση, οι γειτονικές ενότητες αποκτούν περισσότερες εξαρτήσεις — πρέπει να απευθύνονται σε αυτήν την υπερφορτωμένη κλάση για διαφορετικούς σκοπούς, αυξάνοντας τη συζευκτικότητα. Αντίθετα, ο διαχωρισμός σε μικρές κλάσεις υψηλής συνοχής μειώνει τον αριθμό των σημείων αλληλεπίδρασης μεταξύ ενοτήτων.
Στην πράξη αυτό σημαίνει: όταν εξάγετε μια νέα κλάση με λειτουργική συνοχή, ταυτόχρονα απαλλάσσετε άλλες ενότητες από την ανάγκη να γνωρίζουν τις λεπτομέρειες υλοποίησής της. Για παράδειγμα, εξάγοντας το EncryptionManager σε ξεχωριστή κλάση με λειτουργική συνοχή, δίνετε σε άλλες ενότητες μια απλή διεπαφή encrypt/decrypt χωρίς να χρειάζεται να κατανοούν τις λεπτομέρειες του αλγορίθμου κρυπτογράφησης.
// Χαμηλή συνοχή — η κλάση κάνει τα πάντα ταυτόχρονα
class UserManager {
fun fetchAndSaveUser(id: String) { }
fun parseUserJson(json: String): User { }
fun displayUserName(user: User): String { }
fun validateEmail(email: String): Boolean { }
}
// Υψηλή συνοχή — κάθε κλάση λύνει ένα πρόβλημα
class UserRepository {
fun fetchUser(id: String): User { }
}
class UserJsonParser {
fun parse(json: String): User { }
}
class UserNameFormatter {
fun format(user: User): String { }
}
class EmailValidator {
fun isValid(email: String): Boolean { }
}
Το παράδειγμα δείχνει τη διαφορά: το UserManager έχει λογική συνοχή — όλες οι μέθοδοι αφορούν χρήστες, αλλά κάθε μία κάνει εντελώς διαφορετική δουλειά. Μετά την αναδιάρθρωση, κάθε κλάση έχει λειτουργική συνοχή, και η συζευκτικότητα μειώνεται επειδή άλλες ενότητες εξαρτώνται μόνο από την κλάση που χρειάζονται, όχι από ολόκληρο το UserManager.
LCOM (Lack of Cohesion of Methods) — η πιο γνωστή μετρική για τη μέτρηση συνοχής μιας κλάσης. Το LCOM υπολογίζει πόσα ζεύγη μεθόδων δεν χρησιμοποιούν κοινά πεδία. Η τιμή 0 σημαίνει ιδανική συνοχή (όλες οι μέθοδοι εργάζονται με τα ίδια πεδία), η υψηλή τιμή σημαίνει χαμηλή συνοχή. Το LCOM4 (βελτιωμένη έκδοση) λαμβάνει υπόψη τις μεταβατικές συνδέσεις μέσω άλλων μεθόδων.
Στην ανάπτυξη Android, μετρικές συνοχής μπορούν να ληφθούν μέσω του Detekt με τον κανόνα TooManyFunctions. Κλάσεις με δέκα μεθόδους που χρησιμοποιούν διαφορετικές ομάδες πεδίων, πιθανότατα έχουν χαμηλή συνοχή. Στο iOS, το SwiftLint έχει κανόνες file_length και function_body_length — έμμεσοι δείκτες: μεγάλα αρχεία και μέθοδοι συχνά υποδηλώνουν χαμηλή συνοχή.
Χειροκίνητος τρόπος αξιολόγησης: κάντε την ερώτηση «Αυτή η κλάση θα αλλάξει για έναν λόγο ή για πολλούς;» Αν μπορείτε να αναφέρετε περισσότερους από έναν ανεξάρτητους λόγους — η κλάση έχει χαμηλή συνοχή. Δεύτερο τεστ: «Μπορεί αυτή η κλάση να χωριστεί σε δύο ανεξάρτητες κλάσεις;» Αν ναι — κάντε το. Ο τακτικός έλεγχος συνοχής στο code review αποτρέπει την εμφάνιση God κλάσεων και μειώνει το τεχνικό χρέος.
Πρώτο βήμα — εφαρμόστε την Αρχή της Μοναδικής Ευθύνης. Κάθε κλάση πρέπει να έχει μία σαφή ευθύνη. Αν σε μια κλάση υπάρχει μέθοδος που δεν ανήκει στην κύρια λειτουργία της, μεταφέρετέ την σε ξεχωριστή κλάση. Η τεχνική Extract Class ή Extract Delegate στο IDE αυτοματοποιεί αυτή τη διαδικασία. Μετά την εξαγωγή, ελέγξτε αν η αρχική κλάση έγινε πιο εστιασμένη.
Δεύτερο βήμα — χρησιμοποιήστε το μοτίβο Facade για απλοποίηση της διεπαφής. Αν μια κλάση παρέχει 20 μεθόδους, από τις οποίες οι πελάτες χρησιμοποιούν μόνο 3-4, ίσως η κλάση έχει χαμηλή συνοχή — παρέχει πάρα πολλή διαφορετική λειτουργικότητα. Ομαδοποιήστε τις μεθόδους ανά θέμα και εξάγετε ξεχωριστές κλάσεις για κάθε ομάδα, ενώ την αρχική κλάση κάντε την πρόσοψη ή διαγράψτε την.
Τρίτο βήμα — δώστε προσοχή στις ομάδες πεδίων. Αν μια κλάση έχει πεδία που χρησιμοποιούνται μόνο από ένα μέρος των μεθόδων — αυτό είναι ένδειξη χαμηλής συνοχής. Χωρίστε την κλάση ανά ομάδες πεδίων. Για παράδειγμα, αν η κλάση περιέχει πεδία userRepository, networkClient και analyticsTracker, αλλά οι μέθοδοι της πρώτης ομάδας χρησιμοποιούν μόνο το userRepository, ενώ της δεύτερης μόνο το networkClient — πρόκειται για δύο διαφορετικές κλάσεις.
Τέταρτο βήμα — αποφύγετε τη δημιουργία «βοηθητικών» κλάσεων με αυθαίρετες static μεθόδους. Κάθε static μέθοδος που βρίσκεται σε μια κλάση Utils ή Helpers είναι υποψήφια για εξαγωγή σε εξειδικευμένη κλάση. Το FormatUtils.dateToString είναι καλύτερα να μεταφερθεί στο DateFormatter, και το ValidationUtils.isValidEmail — στο EmailValidator. Αυτό αυξάνει τη συνοχή κάθε κλάσης και κάνει τον κώδικα αυτο-τεκμηριωμένο.
Συχνές ερωτήσεις
Σχεδόν πάντα. Η λειτουργική συνοχή κάνει τον κώδικα κατανοητό και προβλέψιμο. Ωστόσο, η υπερβολή μπορεί να οδηγήσει σε υπερβολικό κατακερματισμό: όταν για κάθε λειτουργία δημιουργείται ξεχωριστή κλάση, και η αρχιτεκτονική γίνεται υπερβολικά πολύπλοκη. Ισορροπία — μερικές κλάσεις ανά λειτουργία, η καθεμία με λειτουργική συνοχή.
Cohesion — μετρική εσωτερικής συνοχής μιας ενότητας ή κλάσης. Modularity — αρχιτεκτονική αρχή, κατά την οποία η εφαρμογή χωρίζεται σε φυσικές ενότητες. Η υψηλή συνοχή αποτελεί στόχο κατά τον σχεδιασμό τόσο μεμονωμένων κλάσεων όσο και ολόκληρων ενοτήτων.
Detekt για Android και Xcode Analyzer για iOS επισημαίνουν κλάσεις με ύποπτα μεγάλο αριθμό μεθόδων ή πεδίων. Το IntelliJ IDEA και το AppCode έχουν οπτικοποίηση εξαρτήσεων — μπορείτε να δείτε το γράφημα συνδέσεων και να εντοπίσετε κλάσεις με χαμηλή συνοχή. Το SonarQube υπολογίζει μετρικές LCOM αυτόματα.
Ναι. Μια διεπαφή με μεθόδους connect, disconnect και isConnected έχει υψηλή συνοχή — όλες οι μέθοδοι σχετίζονται με τη διαχείριση σύνδεσης. Μια διεπαφή με μεθόδους connect, parseData και renderUI έχει χαμηλή συνοχή. Η Αρχή Διαχωρισμού Διεπαφών (Interface Segregation — SOLID) απαιτεί τη δημιουργία εξειδικευμένων διεπαφών με υψηλή συνοχή.
Κάντε τρεις ερωτήσεις: μπορείτε να περιγράψετε τον σκοπό της κλάσης με μία πρόταση; Υποστηρίζουν όλες οι μέθοδοι αυτόν τον σκοπό; Υπάρχουν πεδία στην κλάση που δεν χρησιμοποιούνται από μέρος των μεθόδων; Αν η απάντηση σε οποιαδήποτε ερώτηση είναι αρνητική — η συνοχή είναι χαμηλή και η κλάση αξίζει να χωριστεί.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης