Επεξήγηση Clean Code - Μια πρακτική εισαγωγή στο Clean Coding για αρχάριους

Κάθε ανόητος μπορεί να γράψει κώδικα που μπορεί να καταλάβει ένας υπολογιστής. Οι καλοί προγραμματιστές γράφουν κώδικα που οι άνθρωποι μπορούν να καταλάβουν. "- Martin Fowler

Το γράψιμο καθαρού, κατανοητού και διατηρήσιμου κώδικα είναι μια ικανότητα που είναι ζωτικής σημασίας για κάθε προγραμματιστή.

Σε αυτήν την ανάρτηση, θα εξετάσουμε τις πιο σημαντικές αρχές για τη βελτίωση της ποιότητας του κώδικα και θα σας δώσω παραδείγματα κώδικα για καθένα από αυτά.

Τα περισσότερα παραδείγματα λαμβάνονται από τον Robert J. Martin's Clean Code . Είναι κλασικό προγραμματισμού και σας προτείνω να διαβάσετε ολόκληρο το κείμενο όταν έχετε χρόνο.

Πώς να ονομάσετε μεταβλητές (και άλλα πράγματα)

"Υπάρχουν μόνο δύο σκληρά πράγματα στην Επιστήμη των Υπολογιστών: η ακύρωση της προσωρινής μνήμης και η ονομασία πραγμάτων." - Φιλ Κάρλτον

Υπάρχει ένας λόγος για τον οποίο δεν χρησιμοποιούμε διευθύνσεις μνήμης και έχουμε ονόματα: τα ονόματα είναι πολύ πιο εύκολο να θυμηθούν. Και, το πιο σημαντικό, μπορούν να σας δώσουν περισσότερες πληροφορίες σχετικά με τη μεταβλητή, ώστε κάποιος άλλος να καταλάβει τη σημασία της.

Μπορεί να χρειαστεί λίγος χρόνος για να βρείτε ένα καλό όνομα, αλλά θα εξοικονομήσετε εσάς και την ομάδα σας ακόμη περισσότερο χρόνο στο μέλλον. Και είμαι βέβαιος ότι οι περισσότεροι αναγνώστες αντιμετώπισαν την κατάσταση όπου επισκέπτεστε τον κωδικό σας μόνο λίγους μήνες αργότερα και δυσκολεύεστε να καταλάβετε τι κάνατε πριν.

Πώς να δημιουργήσετε νόημα ονόματα

Μην χρησιμοποιείτε σχόλια για να εξηγήσετε γιατί χρησιμοποιείται μια μεταβλητή. Εάν ένα όνομα απαιτεί ένα σχόλιο, τότε θα πρέπει να αφιερώσετε χρόνο για να μετονομάσετε αυτήν τη μεταβλητή αντί να γράψετε ένα σχόλιο.

"Ένα όνομα πρέπει να σας πει γιατί υπάρχει, τι κάνει και πώς χρησιμοποιείται. Εάν ένα όνομα απαιτεί σχόλιο, τότε το όνομα δεν αποκαλύπτει την πρόθεσή του." - Clean Code

Κακό:

var d; // elapsed time in days

Έχω δει αυτόν τον τύπο κώδικα πολλές φορές. Είναι μια κοινή παρανόηση ότι θα πρέπει να κρύψετε το χάος σας με σχόλια. Μην χρησιμοποιείτε γράμματα όπως τα x, y, a ή b ως μεταβλητά ονόματα, εκτός εάν υπάρχει καλός λόγος (οι μεταβλητές βρόχου αποτελούν εξαίρεση σε αυτό).

Καλός:

var elapsedTimeInDays; var daysSinceCreation; var daysSinceModification;

Αυτά τα ονόματα είναι πολύ καλύτερα. Σας λένε τι μετράται και τη μονάδα αυτής της μέτρησης.

Αποφύγετε την παραπληροφόρηση

Προσέξτε τις λέξεις που σημαίνουν κάτι συγκεκριμένο. Μην αναφέρεται σε ομαδοποίηση λογαριασμών ως Λίστα λογαριασμών, εκτός εάν ο τύπος του είναι στην πραγματικότητα Λίστα. Η λέξη έχει μια συγκεκριμένη έννοια και μπορεί να οδηγήσει σε ψευδή συμπεράσματα.

Ακόμα κι αν ο τύπος είναι μια λίστα, οι λογαριασμοί είναι ένα απλούστερο και καλύτερο όνομα.

Κακό:

var accountList = [];

Καλός:

var accounts = []

Αποφύγετε τις λέξεις θορύβου

Οι λέξεις θορύβου είναι οι λέξεις που δεν προσφέρουν επιπλέον πληροφορίες σχετικά με τη μεταβλητή. Είναι περιττές και πρέπει να αφαιρεθούν.

Μερικές δημοφιλείς λέξεις θορύβου είναι:

  • Το (πρόθεμα)
  • Πληροφορίες
  • Δεδομένα
  • Μεταβλητός
  • Αντικείμενο
  • Διευθυντής

Εάν το μάθημά σας ονομάζεται UserInfo, μπορείτε απλώς να καταργήσετε τις Πληροφορίες και να το κάνετε Χρήστη. Η χρήση του BookData αντί του βιβλίου ως όνομα κλάσης είναι απλώς μη-brainer, καθώς η τάξη αποθηκεύει τα δεδομένα ούτως ή άλλως.

Μπορείτε επίσης να διαβάσετε την ανάρτηση ιστολογίου του Jeff Atwood σχετικά με την ονομασία SomethingManager εδώ.

Χρησιμοποιήστε προφανή ονόματα

Εάν δεν μπορείτε να προφέρετε ένα όνομα, δεν μπορείτε να το συζητήσετε χωρίς να ακούτε ανόητο.

Κακό:

const yyyymmdstr = moment().format("YYYY/MM/DD"); 

Καλός:

const currentDate = moment().format("YYYY/MM/DD");

Χρησιμοποιήστε ονόματα με δυνατότητα αναζήτησης

Αποφύγετε τη χρήση μαγικών αριθμών στον κωδικό σας. Επιλέξτε για σταθερές αναζητήσεις, που ονομάζονται σταθερές. Μην χρησιμοποιείτε ονόματα με ένα γράμμα για σταθερές, καθώς μπορούν να εμφανίζονται σε πολλά μέρη και επομένως δεν είναι εύκολα αναζητήσιμα.

Κακό:

if (student.classes.length < 7) { // Do something }

Καλός:

if (student.classes.length < MAX_CLASSES_PER_STUDENT) { // Do something }

Αυτό είναι πολύ καλύτερο επειδή το MAX_CLASSES_PER_STUDENT μπορεί να χρησιμοποιηθεί σε πολλά μέρη σε κώδικα. Εάν πρέπει να το αλλάξουμε σε 6 στο μέλλον, μπορούμε απλά να αλλάξουμε τη σταθερά.

Το κακό παράδειγμα δημιουργεί ερωτηματικά στο μυαλό του αναγνώστη, όπως ποια είναι η σημασία του 7;

Θα πρέπει επίσης να χρησιμοποιήσετε τις σταθερές συμβάσεις ονομασίας και δήλωσης της γλώσσας σας, όπως το ιδιωτικό στατικό τελικό σε Java ή το const σε JavaScript.

Να είναι συνεπής

Ακολουθήστε τη μία λέξη για κάθε κανόνα έννοιας . Μην χρησιμοποιείτε τη λήψη , ανάκτηση και λήψη για την ίδια λειτουργία σε διαφορετικές κατηγορίες. Επιλέξτε ένα από αυτά και χρησιμοποιήστε το σε όλο το έργο, ώστε τα άτομα που διατηρούν τη βάση κώδικα ή τους πελάτες του API σας να μπορούν εύκολα να βρουν τις μεθόδους που αναζητούν.

Πώς να γράψετε συναρτήσεις

Κρατήστε τα μικρά

Οι λειτουργίες πρέπει να είναι μικρές, πολύ μικρές. Θα πρέπει σπάνια να έχουν μήκος 20 γραμμών. Όσο περισσότερο γίνεται μια λειτουργία, είναι πιο πιθανό να κάνει πολλά πράγματα και να έχει παρενέργειες.

Βεβαιωθείτε ότι κάνουν μόνο ένα πράγμα

Οι λειτουργίες πρέπει να κάνουν ένα πράγμα. Πρέπει να το κάνουν καλά. Πρέπει να το κάνουν μόνο. - Clean Code

Your functions should do only one thing. If you follow this rule, it is guaranteed that they will be small. The only thing that function does should be stated in its name.

Sometimes it is hard to look at the function and see if it is doing multiple things or not. One good way to check is to try to extract another function with a different name. If you can find it, that means it should be a different function.

This is probably the most important concept in this article, and it will take some time to get used to. But once you get the hang of it, your code will look much more mature, and it will be more easily refactorable, understandable, and testable for sure.

Encapsulate Conditionals in Functions

Refactoring the condition and putting it into a named function is a good way to make your conditionals more readable.

Here is a piece of code from a school project of mine. This code is responsible for inserting a chip on the board of the Connect4 game.

The isValidInsertion method takes care of checking the validity of the column number and allows us the focus on the logic for inserting the chip instead.

public void insertChipAt(int column) throws Exception { if (isValidInsertion(column)) { insertChip(column); boardConfiguration += column; currentPlayer = currentPlayer == Chip.RED ? Chip.YELLOW : Chip.RED; } else  if (!columnExistsAt(column)) throw new IllegalArgumentException(); else if (isColumnFull(column - 1)  }

Here is the code for isValidInsertion, if you are interested.

 private boolean isValidInsertion(int column) { boolean columnIsAvailable = column = 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS; boolean gameIsOver = getWinner() != Chip.NONE; return columnIsAvailable && !gameIsOver; } 

Without the method, if condition would look like this:

if (column = 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS && getWinner() != Chip.NONE)

Gross, right? I agree.

Fewer Arguments

Functions should have two or fewer arguments, the fewer the better. Avoid three or more arguments where possible.

Arguments make it harder to read and understand the function. They are even harder from a testing point of view, since they create the need to write test cases for every combination of arguments.

Do not use Flag Arguments

A flag argument is a boolean argument that is passed to a function. Two different actions are taken depending on the value of this argument.

For example, say there is a function that is responsible for booking tickets to a concert and there are 2 types of users: Premium and Regular. You can have code like this:

 public Booking book (Customer aCustomer, boolean isPremium) { if(isPremium) // logic for premium book else // logic for regular booking }

Flag arguments naturally contradict the principle of single responsibility. When you see them, you should consider dividing the function into two.

Do Not Have Side Effects

Side effects are unintended consequences of your code. They may be changing the passed parameters, in case of passing by reference, or maybe changing a global variable.

The key point is, they promised to do another thing and you need to read the code carefully to notice the side-effect. They can result in some nasty bugs.

Here is an example from the book:

public class UserValidator { private Cryptographer cryptographer; public boolean checkPassword(String userName, String password) { User user = UserGateway.findByName(userName); if (user != User.NULL) { String codedPhrase = user.getPhraseEncodedByPassword(); String phrase = cryptographer.decrypt(codedPhrase, password); if ("Valid Password".equals(phrase)) { Session.initialize(); return true; } } return false; } }

Can you see the side-effect of this function?

It is checking the password, but when the password is valid, it is also initializing the session which is a side-effect.

You can change the name of the function to something like checkPasswordAndInitializeSession to make this effect explicit. But when you do that, you should notice that your function is actually doing two things and you should not initialize the session here.

Don't Repeat Yourself

Code repetition may be the root of all evil in software. Duplicate code means you need to change things in multiple places when there is a change in logic and it is very error prone.

Use your IDE's refactoring features and extract a method whenever you come across a repeated code segment.

Bonus

Do not leave code in comments

Please, do not. This one is serious because others who see the code will be afraid to delete it because they do not know if it is there for a reason. That commented out code will stay there for a long time. Then when variable names or method names change, it gets irrelevant but still nobody deletes it.

Just delete it. Even if it was important, there is version control for that. You can always find it.

Know your language's conventions

You should know your language's conventions in terms of spacing, comments, and naming things. There are style guides available for many languages.

For example, you should use camelCase in Java but snake_case in Python. You put opening braces on a new line in C# but you put them on the same line in Java and JavaScript.

These things change from language to language and there is no universal standard.

Ακολουθούν ορισμένοι χρήσιμοι σύνδεσμοι για εσάς:

  • Οδηγός στυλ Python
  • Οδηγός στυλ Javascript της Google
  • Οδηγός στυλ Google Java

συμπέρασμα

Η καθαρή κωδικοποίηση δεν είναι μια ικανότητα που μπορεί να αποκτηθεί εν μία νυκτί. Είναι μια συνήθεια που πρέπει να αναπτυχθεί διατηρώντας αυτές τις αρχές κατά νου και εφαρμόζοντας τις όποτε γράφετε κώδικα.

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

Εάν ενδιαφέρεστε να διαβάσετε περισσότερα άρθρα όπως αυτό, μπορείτε να εγγραφείτε στο ιστολόγιό μου.