Η συγγραφή ενός ωραίου, καλά σχεδιασμένου και εμφανίσιμου κώδικα δεν είναι τόσο δύσκολη όσο φαίνεται. Απαιτεί λίγη προσπάθεια για να γνωρίσετε τους βασικούς κανόνες και απλά να τους χρησιμοποιήσετε στον κώδικά σας. Και δεν χρειάζεται να τα κάνετε όλα με τη μία, αλλά καθώς αισθάνεστε άνετα με το ένα πράγμα προσπαθήστε να σκεφτείτε ένα άλλο, και ούτω καθεξής.
Κατασκευάστε σταθερό, όχι ηλίθιο κώδικα
Ας ξεκινήσουμε με την εισαγωγή των πιο στοιχειωδών κανόνων που ονομάζονται SOLID. Πρόκειται για έναν όρο που περιγράφει μια συλλογή αρχών σχεδιασμού για καλές κωδικός που εφευρέθηκε από τον Robert C. Martin και πώς γίνεται:
S σημαίνει αρχή της ενιαίας ευθύνης
Δηλώνει ότι μια τάξη πρέπει να έχει έναν και μόνο έναν λόγο για να αλλάξει. Με άλλα λόγια, μια κλάση θα πρέπει να έχει μόνο μια δουλειά να κάνει, οπότε θα πρέπει να αποφεύγουμε να γράφουμε μεγάλες κλάσεις με πολλές ευθύνες. Αν η κλάση μας πρέπει να κάνει πολλά πράγματα τότε κάθε ένα από αυτά θα πρέπει να ανατεθεί σε ξεχωριστές.
κλάση Notification
def initialize(params)
@params = params
end
def call
EmailSender.new(message).call
end
private
def message
# κάποια εφαρμογή
end
end
O σημαίνει Αρχή Ανοιχτό/Κλειστό
Δηλώνει ότι θα πρέπει να μπορείτε να επεκτείνετε τη συμπεριφορά μιας κλάσης, χωρίς να την τροποποιείτε. Με άλλα λόγια, θα πρέπει να είναι εύκολο να επεκτείνετε μια κλάση χωρίς να κάνετε καμία τροποποίηση σε αυτήν. Μπορούμε να το πετύχουμε αυτό π.χ. με τη χρήση του προτύπου στρατηγικής ή των διακοσμητών.
κλάση Notification
def initialize(params, sender)
@params = params
@sender = αποστολέας
end
def call
sender.call message
end
private
def message
# κάποια εφαρμογή
end
end
class EmailSender
def call(message)
# κάποια υλοποίηση
end
end
κλάση SmsSender
def call(message)
# κάποια υλοποίηση
end
end
Με αυτόν τον σχεδιασμό, είναι δυνατή η προσθήκη νέων αποστολέων χωρίς αλλαγή κώδικα.
L σημαίνει αρχή υποκατάστασης Liskov
Δηλώνει ότι οι παράγωγες κλάσεις πρέπει να μπορούν να αντικαταστήσουν τις βασικές τους κλάσεις. Με άλλα λόγια, η χρήση των κλάσεων που προέρχονται από τον ίδιο πρόγονο πρέπει να είναι εύκολο να αντικατασταθεί από άλλη απόγονο.
class Καταγραφέας {
info (message) {
console.info(this._prefixFor('info') + message)
}
error (message, err) {
console.error(this._prefixFor('error') + message)
if (err) {
console.error(err)
}
}
_prefixFor (type) {
// κάποια υλοποίηση
}
}
class ScepticLogger extends Logger {
info (message) {
super.info(message)
console.info(this._prefixFor('info') + 'Και αυτό είναι το μόνο που είχα να πω.')
}
error (message, err) {
super.error(message, err)
console.error(this._prefixFor('error') + 'Σιγά το πράγμα!')
}
}
Μπορούμε εύκολα να αντικαταστήσουμε το όνομα της κλάσης, επειδή και οι δύο έχουν ακριβώς την ίδια διεπαφή χρήσης.
Εννοώ την αρχή του διαχωρισμού διεπαφών
Δηλώνει ότι πρέπει να δημιουργείτε λεπτόκοκκες διεπαφές που αφορούν τον πελάτη. Τι είναι μια διεπαφή; Είναι ένας παρεχόμενος τρόπος χρήσης κάποιου μέρους του κώδικα. Έτσι μια παραβίαση αυτού του κανόνα θα μπορούσε να είναι π.χ. μια κλάση με πάρα πολλές μεθόδους καθώς και μια μέθοδος με πάρα πολλές επιλογές ορίσματος. Ένα καλό παράδειγμα για να οπτικοποιήσουμε αυτή την αρχή είναι ένα μοτίβο αποθετηρίου, όχι μόνο επειδή συχνά βάζουμε πολλές μεθόδους σε μια μόνο κλάση αλλά και επειδή αυτές οι μέθοδοι είναι εκτεθειμένες στον κίνδυνο να δεχτούν πάρα πολλά ορίσματα.
# όχι το καλύτερο παράδειγμα αυτή τη φορά
class NotificationRepository
def find_all_by_ids(ids:, info:)
notifications = Notification.where(id: ids)
info ? notifications.where(type: :info) : notifications
end
end
# και ένα καλύτερο
class NotificationRepository
def find_all_by_ids(ids:)
Notification.where(id: ids)
end
def find_all_by_ids_info(ids:)
find_all_by_ids(ids).where(type: :info)
end
end
D σημαίνει αρχή αντιστροφής της εξάρτησης
Δηλώνει ότι θα πρέπει να βασίζεστε σε αφηρημένες έννοιες, όχι σε συγκεκριμένες. Με άλλα λόγια, μια κλάση που χρησιμοποιεί μια άλλη δεν θα πρέπει να εξαρτάται από τις λεπτομέρειες υλοποίησής της, το μόνο που έχει σημασία είναι η διεπαφή χρήστη.
κλάση Notification
def initialize(params, sender)
@params = params
@sender = αποστολέας
end
def call
sender.call message
end
private
def message
# κάποια εφαρμογή
end
end
Το μόνο που χρειάζεται να γνωρίζουμε για το αντικείμενο sender είναι ότι εκθέτει τη μέθοδο `call` η οποία αναμένει το μήνυμα ως όρισμα.
Όχι ο καλύτερος κώδικας
Είναι επίσης πολύ σημαντικό να γνωρίζουμε τα πράγματα που πρέπει να αποφεύγονται αυστηρά κατά τη συγγραφή κώδικα, οπότε ακολουθεί άλλη μια συλλογή με ηλίθιες αρχές που καθιστούν τον κώδικα μη συντηρήσιμο, δύσκολο για δοκιμή και επαναχρησιμοποίηση.
S σημαίνει Singleton
Τα singletons συχνά θεωρούνται αντι-πρότυπα και γενικά πρέπει να αποφεύγονται. Αλλά το κύριο πρόβλημα με αυτό το μοτίβο είναι ότι αποτελεί ένα είδος δικαιολογίας για τις παγκόσμιες μεταβλητές/μεθόδους και θα μπορούσε γρήγορα να γίνει υπερβολική χρήση από τους προγραμματιστές.
T σημαίνει στεγανή ζεύξη
Διατηρείται όταν μια αλλαγή σε μια ενότητα απαιτεί επίσης αλλαγές σε άλλα μέρη της εφαρμογής.
U σημαίνει Untestability
Αν ο κώδικάς σας είναι καλός, τότε η συγγραφή δοκιμών θα πρέπει να ακούγεται διασκεδαστική και όχι εφιαλτική.
P σημαίνει πρόωρη βελτιστοποίηση
Η λέξη πρόωρη είναι το κλειδί εδώ, αν δεν το χρειάζεστε τώρα, τότε είναι χάσιμο χρόνου. Είναι προτιμότερο να επικεντρωθείτε σε έναν καλό, καθαρό κώδικα παρά σε κάποιες μικρο-βελτιστοποιήσεις - οι οποίες γενικά προκαλούν πιο πολύπλοκο κώδικα.
Εννοώ την περιγραφική ονομασία
Είναι το δυσκολότερο πράγμα στη συγγραφή καλού κώδικα, αλλά να θυμάστε ότι δεν είναι μόνο για το υπόλοιπο του προγράμματός σας. ομάδα αλλά και για το μέλλον σας, οπότε φερθείτε σωστά 🙂 Είναι καλύτερο να γράψετε ένα μακρύ όνομα για μια μέθοδο που λέει όμως τα πάντα, παρά ένα σύντομο και αινιγματικό.
D σημαίνει Διπλασιασμός
Ο κύριος λόγος για την επανάληψη στον κώδικα είναι η τήρηση της αρχής της στενής σύζευξης. Αν ο κώδικάς σας είναι στενά συνδεδεμένος, απλά δεν μπορείτε να τον επαναχρησιμοποιήσετε και εμφανίζεται διπλός κώδικας, οπότε ακολουθήστε την DRY και μην επαναλαμβάνεστε.
Δεν είναι πραγματικά σημαντικό αυτή τη στιγμή
Θα ήθελα επίσης να αναφέρω δύο πολύ σημαντικά πράγματα που συχνά παραλείπονται. Θα πρέπει να έχετε ακούσει για το πρώτο - είναι το YAGNI που σημαίνει: δεν θα το χρειαστείτε. Κατά καιρούς παρατηρώ αυτό το πρόβλημα κάνοντας code review ή ακόμα και γράφοντας τον δικό μου κώδικα, αλλά θα πρέπει να αλλάξουμε τη σκέψη μας σχετικά με την υλοποίηση ενός χαρακτηριστικού. Θα πρέπει να γράψουμε ακριβώς τον κώδικα που χρειαζόμαστε αυτή τη στιγμή, όχι περισσότερο ή λιγότερο. Θα πρέπει να έχουμε στο μυαλό μας ότι όλα αλλάζουν πολύ γρήγορα (ειδικά οι απαιτήσεις της εφαρμογής), οπότε δεν υπάρχει λόγος να σκεφτόμαστε ότι κάτι κάποια στιγμή θα μας φανεί χρήσιμο. Μην σπαταλάτε τον χρόνο σας.
Δεν καταλαβαίνω
Και το τελευταίο πράγμα, που δεν είναι πραγματικά προφανές υποθέτω, και μπορεί να είναι αρκετά αμφιλεγόμενο για κάποιους, είναι ένας περιγραφικός κωδικός. Δεν εννοώ με αυτό μόνο τη χρήση των σωστών ονομάτων για κλάσεις, μεταβλητές ή μεθόδους. Είναι πραγματικά, πραγματικά καλό όταν ολόκληρος ο κώδικας είναι αναγνώσιμος από την πρώτη ματιά. Ποιος είναι ο σκοπός του πολύ σύντομου κώδικα, ενώ είναι όσο πιο αινιγματικός γίνεται και κανείς δεν ξέρει τι κάνει, εκτός από αυτόν που τον έγραψε; Κατά τη γνώμη μου, είναι καλύτερα να γράψετε μερικά charsδηλώσεις όρωνκάτι άλλο περισσότερο από μια λέξη και μετά χθες να κάθεται και να αναρωτιέται: περιμένετε ποιο είναι το αποτέλεσμα, πώς συνέβη, και ούτω καθεξής.
const params = [
{
movies: [
{ title: 'The Shawshank Redemption' },
{ title: 'One Flew Over the Cuckoo's Nest' }
]
},
{
movies: [
{ title: 'Saving Private Ryan' },
{ title: 'Pulp Fiction' },
{ title: 'The Shawshank Redemption' },
]
}
]
// πρώτη πρόταση
function uniqueMovieTitlesFrom (params) {
const titles = params
.map(param => param.movies)
.reduce((prev, nex) => prev.concat(next))
.map(movie => movie.title)
return [...new Set(titles)]
}
// δεύτερη πρόταση
function uniqueMovieTitlesFrom (params) {
const titles = {}
params.forEach(param => {
param.movies.forEach(movie => titles[movie.title] = true)
})
return Object.keys(titles)
}
Συνοψίζοντας
Όπως μπορείτε να δείτε, υπάρχουν πολλοί κανόνες που πρέπει να θυμάστε, αλλά όπως ανέφερα στην αρχή η συγγραφή ενός ωραίου κώδικα είναι θέμα χρόνου. Αν αρχίσετε να σκέφτεστε μια βελτίωση των συνηθειών σας στον προγραμματισμό, τότε θα δείτε ότι θα ακολουθήσει ένας άλλος καλός κανόνας, γιατί όλα τα καλά πράγματα προκύπτουν από μόνα τους, όπως ακριβώς και τα κακά.
Διαβάστε περισσότερα:
Τι είναι το Ruby on Jets και πώς να δημιουργήσετε μια εφαρμογή χρησιμοποιώντας το;
1TP57Ημερολόγιο. Ένα νέο έργο του Codest που βασίζεται στο Vue.js
Η εβδομαδιαία έκθεση του Codest με τα καλύτερα άρθρα τεχνολογίας. Δημιουργία λογισμικού για 50 εκατ. ταυτόχρονες υποδοχές (10)