DRY (Don't Repeat Yourself) — μια θεμελιώδης αρχή ανάπτυξης που διατυπώθηκε από τους Andy Hunt και Dave Thomas στο βιβλίο “The Pragmatic Programmer”. Ορίζει: κάθε κομμάτι γνώσης σε ένα σύστημα πρέπει να έχει μια μοναδική, σαφή, έγκυρη αναπαράσταση. Σύμφωνα με το The Pragmatic Programmer, 20th Anniversary Edition, η παραβίαση του DRY οδηγεί στο ότι η αλλαγή ενός στοιχείου απαιτεί διορθώσεις σε δεκάδες σημεία και κάθε παραλειπόμενο τμήμα γίνεται πηγή σφάλματος.
Κύρια σημεία
DRY (Don't Repeat Yourself) — μια αρχή ανάπτυξης που απαιτεί μοναδική αποθήκευση κάθε στοιχείου γνώσης στο έργο. Αυτό σημαίνει ότι κάθε λογική, διαμόρφωση ή μεταδεδομένα πρέπει να υπάρχουν ακριβώς σε ένα μέρος.
Ο όρος εισήχθη από τους Andy Hunt και Dave Thomas το 1999 στο βιβλίο “The Pragmatic Programmer”. Οι συγγραφείς όρισαν το DRY ως “κάθε κομμάτι γνώσης πρέπει να έχει μια μοναδική, συνεπή αναπαράσταση στο σύστημα”. Το αντίθετο του DRY — η προσέγγιση WET (Write Everything Twice), όπου η επανάληψη θεωρείται κανόνας.
Σύμφωνα με έρευνα του University of California, Davis (2019), τα έργα με υψηλό επίπεδο επανάληψης κώδικα ξοδεύουν 42% περισσότερο χρόνο για τη διόρθωση σφαλμάτων. Ο λόγος είναι ότι ο προγραμματιστής πρέπει να βρει και να τροποποιήσει όλα τα αντίγραφα του ίδιου τμήματος — και στη χειροκίνητη αναζήτηση, οι παραλείψεις είναι αναπόφευκτες.
Εφαρμόστε το DRY ως κριτήριο ποιότητας κώδικα. Αν παρατηρήσετε ότι το ίδιο μοτίβο εμφανίζεται τρεις φορές στο έργο — εξάγετέ το σε μια αφαίρεση, χωρίς να περιμένετε την τέταρτη επανάληψη.
Single Responsibility Principle (SRP) από το SOLID δηλώνει ότι μια κλάση πρέπει να έχει έναν λόγο για αλλαγή. Το DRY είναι ευρύτερο: καλύπτει όχι μόνο κλάσεις, αλλά και δεδομένα, διαμόρφωση, τεκμηρίωση, ακόμη και επιχειρηματικούς κανόνες. Το SRP αφορά τα όρια ευθύνης, το DRY — το απαράδεκτο της αντιγραφής.
Στην ανάπτυξη κινητών, αυτή η διαφορά είναι ιδιαίτερα εμφανής. Αν ο ίδιος επιχειρηματικός κανόνας (υπολογισμός φόρου, μορφοποίηση ημερομηνίας) επαναλαμβάνεται στα τμήματα Android και iOS — αυτό είναι παραβίαση του DRY, ακόμα κι αν το SRP τυπικά τηρείται εντός κάθε πλατφόρμας. Λύση — εξαγωγή της κοινής λογικής σε ένα κοινόχρηστο module (KMM, C++).
Σύμφωνα με την αναφορά Google Android Architecture Guidelines (2023), οι ομάδες που χρησιμοποιούν κοινά modules για επιχειρηματική λογική μειώνουν τον αριθμό σφαλμάτων κατά την αλλαγή απαιτήσεων κατά 37% σε σύγκριση με έργα με επανάληψη λογικής μεταξύ πλατφορμών.
Επανάληψη — η κύρια πηγή τεχνικού χρέους σε έργα κινητών. Κάθε αντίγραφο κώδικα δημιουργεί μια κρυφή εξάρτηση: για να αλλάξετε συμπεριφορά, πρέπει να βρείτε και να ενημερώσετε όλα τα αντίγραφα. Η παράλειψη έστω και ενός σημαίνει σφάλμα.
Εξετάστε μια κλασική κατάσταση: σε μια εφαρμογή Android, η μορφοποίηση ημερομηνίας γίνεται σε τρία διαφορετικά Activity. Κατά τη μετάβαση σε νέα μορφή (π.χ. ISO 8601), ο προγραμματιστής διορθώνει δύο αρχεία, ξεχνά το τρίτο — και ο χρήστης βλέπει την ημερομηνία σε παλιά μορφή. Η βαθμολογία χρηστών της εφαρμογής πέφτει και η εύρεση του σφάλματος διαρκεί διπλάσιο χρόνο.
Η έρευνα Google Research (2020) έδειξε: το 68% των κρίσιμων σφαλμάτων σε εφαρμογές κινητών σχετίζεται με μη σύγχρονη αλλαγή επαναλαμβανόμενου κώδικα. Το κόστος διόρθωσης ενός τέτοιου σφάλματος σε παραγωγή είναι 4,5 φορές υψηλότερο από ό,τι αν ο κώδικας ήταν ενιαίος από την αρχή.
Χρησιμοποιήστε στατικό αναλυτή (Detekt, SwiftLint) με κανόνες που απαγορεύουν την ανίχνευση copy-paste. Διαμορφώστε το CI ώστε τα pull-request με επανάληψη άνω των N γραμμών να μην περνούν review χωρίς αιτιολόγηση.
Ένα τυπικό anti-pattern — η αντιγραφή του RecyclerView adapter με μικρές αλλαγές. Αντί για έναν καθολικό adapter με διαμόρφωση, οι προγραμματιστές δημιουργούν ξεχωριστή κλάση για κάθε οθόνη. Η αναδόμηση με εξαγωγή μιας κοινής βασικής κλάσης συντομεύει τον κώδικα κατά 30–50%.
// Επανάληψη: δύο ξεχωριστοί adapter
class UserAdapter {
fun bind(item: User) { /* ... */ }
}
class ProductAdapter {
fun bind(item: Product) { /* ... */ }
}
// Αναδόμηση DRY: κοινή βασική κλάση
abstract class BaseAdapter<T> {
abstract fun bind(item: T)
}
class UserAdapter : BaseAdapter<User>() { /* ... */ }
class ProductAdapter : BaseAdapter<Product>() { /* ... */ }
Στο πρώτο παράδειγμα, κάθε adapter υλοποιεί ξανά τον μηχανισμό bind. Κατά την προσθήκη νέας λογικής (αναλυτικά, καταγραφή) θα έπρεπε να τροποποιηθεί κάθε αρχείο. Η βασική κλάση εξαλείφει αυτή την επανάληψη: η κοινή λογική ζει σε ένα μέρος, η ειδική λογική στις παραγόμενες κλάσεις.
Σε έργα iOS, συχνά επαναλαμβάνεται η διαμόρφωση URLSession — κεφαλίδες, χρονικά όρια, διαχείριση σφαλμάτων. Κάθε υπηρεσία δημιουργεί τη δική της συνεδρία με επαναλαμβανόμενες ρυθμίσεις.
// Επανάληψη: κάθε υπηρεσία διαμορφώνει τη συνεδρία από την αρχή
class UserService {
let session = URLSession(configuration: {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return cfg
}())
}
// DRY: ενιαίο εργοστάσιο συνεδριών
struct NetworkConfig {
static var session: URLSession {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return URLSession(configuration: cfg)
}
}
Η εξαγωγή της διαμόρφωσης σε ένα ενιαίο NetworkConfig εγγυάται ότι όλες οι υπηρεσίες χρησιμοποιούν τις ίδιες κεφαλίδες και χρονικά όρια. Μια αλλαγή σε ένα μέρος εφαρμόζεται αυτόματα σε όλα τα αιτήματα — αυτό μειώνει τον κίνδυνο σφαλμάτων κατά την αλλαγή κλειδιού API ή έκδοσης πρωτοκόλλου.
Κληρονομικότητα — ο φυσικός τρόπος εξάλειψης της επανάληψης: η κοινή λογική εξάγεται σε μια βασική κλάση και η ειδική λογική στις παραγόμενες κλάσεις. Ωστόσο, στην ανάπτυξη κινητών, η κατάχρηση κληρονομικότητας δημιουργεί άκαμπτες ιεραρχίες που είναι δύσκολο να συντηρηθούν. Σύνθεση (έγχυση εξαρτήσεων) — μια πιο ευέλικτη εναλλακτική.
Η ανάλυση του Google I/O 2023: Modern Android Architecture έδειξε ότι το 76% των ομάδων της Google προτιμά τη σύνθεση έναντι της κληρονομικότητας για την εξάλειψη της επανάληψης. Αντί για BaseViewModel με δέκα μεθόδους, συνιστάται η εξαγωγή ξεχωριστών κλάσεων UseCase για κάθε επιχειρηματική λειτουργία και η έγχυσή τους όπου χρειάζονται.
Επιλέξτε σύνθεση σε όλες τις περιπτώσεις εκτός από σχέσεις “είναι-ένα”. Αν η κλάση Α είναι εξειδίκευση της κλάσης Β — η κληρονομικότητα είναι κατάλληλη. Αν Α απλώς χρησιμοποιεί τη λειτουργικότητα του Β — χρησιμοποιήστε σύνθεση.
Βοηθητικές κλάσεις (Extensions, Helpers) — ο απλούστερος τρόπος αποφυγής επανάληψης. Τυπικοί υποψήφιοι: μορφοποίηση ημερομηνίας, επικύρωση email, μετατροπή μονάδων, εργασία με SharedPreferences/UserDefaults.
// DRY: ενιαία συνάρτηση μορφοποίησης ημερομηνίας
fun Date.toDisplayFormat(): String {
val sdf = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
return sdf.format(this)
}
// Χρήση σε οποιοδήποτε μέρος της εφαρμογής
textView.text = Date().toDisplayFormat()
Η επέκταση Date.toDisplayFormat() δηλώνεται μία φορά και είναι διαθέσιμη σε ολόκληρο το έργο. Αν χρειαστεί να αλλάξει η μορφή από “dd.MM.yyyy” σε “yyyy-MM-dd” — διόρθωση σε ένα αρχείο, όχι σε κάθε Activity ή Fragment όπου εμφανίζεται μορφοποίηση. Αυτή είναι η ουσία του DRY.
Πολυαρθρωτά έργα Android συχνά επαναλαμβάνουν εκδόσεις εξαρτήσεων σε κάθε build.gradle. Λύση — version catalog (libs.versions.toml), που συγκεντρώνει όλες τις εκδόσεις σε ένα αρχείο.
Σύμφωνα με το Android Developer Documentation (2024), η μετεγκατάσταση σε version catalog μειώνει τις συγκρούσεις εξαρτήσεων κατά 52% και επιταχύνει τη δημιουργία χάρη σε ένα ενιαίο σημείο διορθώσεων.
Εφαρμόστε το version catalog στην αρχή του έργου ή κατά την πρώτη αναδιοργάνωση των modules. Αν το έργο έχει ήδη επανάληψη — αφιερώστε μια μέρα για μετεγκατάσταση: θα αποδώσει στην επόμενη ενημέρωση βιβλιοθηκών.
Πρόωρη αφαίρεση — το πιο συνηθισμένο λάθος αρχαρίων. Ο προγραμματιστής βλέπει δύο παρόμοιες γραμμές κώδικα και αμέσως τις εξάγει σε μια κοινή συνάρτηση. Μετά από ένα μήνα, οι απαιτήσεις αλλάζουν και η κοινή συνάρτηση γεμίζει με παραμέτρους και σημαίες — γίνεται πιο περίπλοκη από την αρχική επανάληψη. Το Rule of Three προστατεύει ακριβώς από αυτό: μην αφαιρείτε αυτό που εμφανίστηκε μία ή δύο φορές.
Ο Martin Fowler στο βιβλίο Refactoring (2019) συνιστά: “Η επανάληψη κώδικα δεν είναι πάντα κακή. Η επανάληψη γνώσης είναι κακή”. Αν δύο γραμμές συμπίπτουν τυχαία αλλά εκφράζουν διαφορετικές έννοιες — δεν είναι επανάληψη, αλλά σύμπτωση. Το Rule of Three βοηθά να διακρίνουμε την τυχαία σύμπτωση από τη συστηματική επανάληψη.
Πριν από την αφαίρεση, αξιολογήστε τη σημασιολογία. Αντιγραμμένος κώδικας με την ίδια σημασία — παραβίαση του DRY. Κώδικας με διαφορετική σημασία αλλά παρόμοια σύνταξη — σύμπτωση που δεν απαιτεί αφαίρεση.
Υπερβολική παραμετροποίηση προκύπτει όταν μία συνάρτηση προσπαθεί να καλύψει όλα τα πιθανά σενάρια μέσω σημαιών και boolean παραμέτρων. Τέτοιος κώδικας παραβιάζει το SRP και γίνεται δυσανάγνωστος. Σύμπτωμα: αν μια συνάρτηση έχει περισσότερες από δύο boolean παραμέτρους — αυτή είναι μια οσμή κώδικα (code smell) υπερβολικής αφαίρεσης.
Αντί για μία συνάρτηση με σημαία useCache: Boolean, καλύτερα να δημιουργήσετε δύο ξεχωριστές συναρτήσεις με σαφή ονόματα: fetchFromNetwork() και fetchFromCache(). Η σαφήνεια είναι σημαντικότερη από τη στεγνή αφαίρεση — αυτό συνάδει με την αρχή KISS.
Αναδομήστε την υπερβολική παραμετροποίηση όταν η συνάρτηση φτάσει 3+ boolean παραμέτρους. Χωρίστε σε ξεχωριστές συναρτήσεις με σαφή ονόματα — κάθε κλήση θα γίνει αυτο-τεκμηριωμένη.
Συχνές ερωτήσεις
DRY (Don't Repeat Yourself) — αρχή που απαιτεί την αποθήκευση κάθε λογικής μονάδας σε ένα μέρος. Αν ο ίδιος κώδικας εμφανίζεται σε πολλά μέρη του έργου — αυτό είναι παραβίαση του DRY. Διόρθωση: εξάγετε την επαναλαμβανόμενη λογική σε μια ξεχωριστή συνάρτηση, κλάση ή module.
WET (Write Everything Twice) — το αντίθετο του DRY, όπου η επανάληψη θεωρείται αποδεκτή. Σε έργα WET, το ίδιο τμήμα κώδικα μπορεί να υπάρχει σε πέντε αντίγραφα και όταν αλλάζουν οι απαιτήσεις, ο προγραμματιστής διορθώνει κάθε αντίγραφο ξεχωριστά. Το WET αυξάνει τον κίνδυνο σφαλμάτων και επιβραδύνει την ανάπτυξη.
Το DRY είναι επιβλαβές στην πρόωρη αφαίρεση: όταν δύο παρόμοια αλλά σημασιολογικά διαφορετικά τμήματα κώδικα συνδυάζονται βίαια σε μία συνάρτηση. Αυτό δημιουργεί περίπλοκο, υπερφορτωμένο με παραμέτρους κώδικα. Το Rule of Three βοηθά στην αποφυγή αυτού του λάθους: αφαιρείτε μόνο μετά την τρίτη επανάληψη.
Στο Android, το DRY εφαρμόζεται μέσω version catalog (libs.versions.toml), κοινών βασικών κλάσεων για adapters, ViewModel factories και χρήσιμων επεκτάσεων Kotlin. Συνιστάται η εξαγωγή επιχειρηματικής λογικής σε κοινόχρηστα modules (KMM) και η χρήση View Binding για την εξάλειψη της επανάληψης findViewById.
Στο iOS, το DRY επιτυγχάνεται μέσω πρωτοκόλλων με προεπιλεγμένη υλοποίηση, κοινών διαμορφώσεων δικτύου (NetworkConfig), εργοστασίων κελιών UICollectionView και πακέτων SPM με κοινή επιχειρηματική λογική. Οι Extensions των τυπικών τύπων (Date, String, URL) μειώνουν την επανάληψη μορφοποίησης και επικύρωσης.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης