A tiszta, érthető és karbantartható kód írása olyan készség, amely minden fejlesztő számára elengedhetetlen.
Ebben a bejegyzésben megvizsgáljuk a kódminőség javításának legfontosabb alapelveit, és mindegyikükhöz kód példákat adok.
A legtöbb példa Robert J. Martin Tiszta kódexéből származik . Ez egy programozási klasszikus, és azt javaslom, olvassa el a teljes szöveget, amikor van ideje.
A változók (és egyéb dolgok) megnevezése
"A számítógéptudományban csak két nehéz dolog van: a gyorsítótár érvénytelenítése és a dolgok elnevezése." - Phil KarltonVan egy oka annak, hogy nem használunk memóriacímeket, és helyette neveket használunk: a neveket sokkal könnyebb felidézni. És ami még fontosabb: több információt adhatnak a változóról, így valaki más is megértheti annak jelentőségét.
A jó név megtalálása eltarthat egy ideig, de a jövőben még több időt takaríthat meg Önnek és csapatának. És biztos vagyok abban, hogy az olvasók többsége szembesült azzal a helyzettel, amikor csak néhány hónappal később keresi fel a kódot, és nehezen érti, mit tett korábban.
Hogyan hozzunk létre értelmes neveket
Ne használjon megjegyzéseket annak magyarázatához, hogy miért használnak változót. Ha egy névhez megjegyzés szükséges, akkor a megjegyzés helyett időt kell fordítania a változó átnevezésére.
"A névnek meg kell mondania, miért létezik, mit csinál és hogyan használjuk. Ha egy névhez megjegyzés szükséges, akkor a név nem fedi fel szándékát." - Tiszta kódRossz:
var d; // elapsed time in days
Annyiszor láttam ilyen típusú kódot. Általános tévhit, hogy a rendetlenséget el kell rejteni megjegyzésekkel. Ne használjon változó névként olyan betűket, mint x, y, a vagy b, kivéve, ha erre jó oka van (a ciklusváltozók ez alól kivételt képeznek).
Jó:
var elapsedTimeInDays; var daysSinceCreation; var daysSinceModification;
Ezek a nevek sokkal jobbak. Megmondják, mit mérnek, és ennek a mértéknek az egységét.
Kerülje a dezinformációt
Legyen óvatos a szavakkal kapcsolatban, amelyek valami konkrétat jelentenek. Ne utaljon a számlák csoportosítására fióklistaként, kivéve, ha annak típusa valójában Lista. A szónak konkrét jelentése van, és hamis következtetésekhez vezethet.
Még akkor is, ha a típus lista, a fiókok egyszerűbb és jobb név.
Rossz:
var accountList = [];
Jó:
var accounts = []
Kerülje a zajszavakat
A zaj szavak azok a szavak, amelyek nem nyújtanak további információt a változóról. Feleslegesek és el kell távolítani.
Néhány népszerű zaj szó:
- Az (előtag)
- Info
- Adat
- Változó
- Tárgy
- Menedzser
Ha az osztályod neve UserInfo, akkor egyszerűen eltávolíthatod az Infót, és felhasználónévé teheted. A BookData használata a Book helyett osztálynévként egyszerűen nem gond, mivel egy osztály mindenképpen tárolja az adatokat.
Itt olvashatja el Jeff Atwood blogbejegyzését is a SomethingManager névadásáról.
Használja a Kiejthető neveket
Ha nem tudsz kiejteni egy nevet, nem beszélheted meg anélkül, hogy butának tűnne.
Rossz:
const yyyymmdstr = moment().format("YYYY/MM/DD");
Jó:
const currentDate = moment().format("YYYY/MM/DD");
Használja a Kereshető neveket
Kerülje a mágikus számok használatát a kódban. Válasszon kereshető, megnevezett konstansokat. Ne használjon konstansokhoz egybetűs neveket, mivel azok sok helyen megjelenhetnek, ezért nem könnyen kereshetők.
Rossz:
if (student.classes.length < 7) { // Do something }
Jó:
if (student.classes.length < MAX_CLASSES_PER_STUDENT) { // Do something }
Ez sokkal jobb, mert a MAX_CLASSES_PER_STUDENT sok helyen használható a kódban. Ha a jövőben 6-ra kell cserélnünk, akkor csak az állandót tudjuk megváltoztatni.
A rossz példa kérdőjeleket hoz létre az olvasó fejében, például mi a fontossága a 7-nek?
Használnia kell a nyelvének állandó elnevezési és deklarációs szokásait is, például a privát statikus végleges Java-ban vagy a const-ot a Java- ban.
Légy következetes
Kövesse az egyes szavakat minden fogalomszabályhoz . Ne használjon letölteni , letölteni, és kap az ugyanezen művelethez a különböző osztályokban. Válasszon egyet közülük, és használja az egész projektben, hogy a kódbázist karbantartó emberek vagy az API kliensei könnyen megtalálják a kívánt módszereket.
Hogyan írhatunk függvényeket
Tartsa őket kicsiben
A funkciók legyenek kicsiek, nagyon kicsiek. Ritkán legyenek 20 sorosak. Minél hosszabb egy funkció, annál valószínűbb, hogy több dolgot végez és mellékhatásai vannak.
Győződjön meg róla, hogy csak egy dolgot csinálnak
A funkcióknak egyet kell tenniük. Jól kellene csinálniuk. Csak nekik kellene. - Tiszta kódYour 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.
Here are some useful links for you:
- Python Style Guide
- Google's Javascript Style Guide
- Google Java Style Guide
Conclusion
Clean coding is not a skill that can be acquired overnight. It is a habit that needs to be developed by keeping these principles in mind and applying them whenever you write code.
Thank you for taking your time to read and I hope it was helpful.
If you are interested in reading more articles like this, you can subscribe to my blog.