Обяснен чист код - Практическо въведение в чистото кодиране за начинаещи

„Всеки глупак може да пишекод, който компютърът може да разбере. Добрите програмисти пишат код, който хората могат да разберат. "- Мартин Фаулър

Писането на чист, разбираем и поддържаем код е умение, което е от решаващо значение за всеки разработчик да овладее.

В този пост ще разгледаме най-важните принципи за подобряване на качеството на кода и ще ви дам примери за всеки от тях.

Повечето примери са взети от Чистия кодекс на Робърт Дж. Мартин . Това е класика за програмиране и ви предлагам да прочетете целия текст, когато имате време.

Как да назовавате променливи (и други неща)

„В Компютърните науки има само две трудни неща: обезсилване на кеша и именуване на неща.“ - Фил Карлтън

Има причина, поради която не използваме адреси в паметта и вместо това имаме имена: имената са много по-лесни за извикване. И по-важното е, че те могат да ви дадат повече информация за променливата, така че някой друг да може да разбере нейното значение.

Може да отнеме известно време, за да намерите добро име, но ще спестите на вас и вашия екип още повече време в бъдеще. И съм сигурен, че повечето читатели са се сблъсквали със ситуацията, когато посещавате кода си само няколко месеца по-късно и трудно разбирате какво сте правили преди.

Как да създадете смислени имена

Не използвайте коментари, за да обясните защо се използва променлива. Ако името изисква коментар, трябва да отделите време, за да преименувате тази променлива, вместо да пишете коментар.

"Името трябва да ви каже защо съществува, какво прави и как се използва. Ако името изисква коментар, името не разкрива намерението си." - Чист код

Лошо:

var d; // elapsed time in days

Виждал съм този тип код толкова много пъти. Често срещано заблуждение е, че трябва да скриете бъркотията си с коментари. Не използвайте букви като x, y, a или b като имена на променливи, освен ако няма основателна причина (променливите на цикъла са изключение от това).

Добре:

var elapsedTimeInDays; var daysSinceCreation; var daysSinceModification;

Тези имена са много по-добри. Те ви казват какво се измерва и мерната единица на това измерване.

Избягвайте дезинформацията

Внимавайте с думи, които означават нещо конкретно. Не посочвайте групиране на акаунти като accountList, освен ако неговият тип всъщност е Списък. Думата има специфично значение и може да доведе до неверни заключения.

Дори ако типът е списък, акаунтите са по-просто и по-добро име.

Лошо:

var accountList = [];

Добре:

var accounts = []

Избягвайте шумовите думи

Думите с шум са думите, които не предлагат никаква допълнителна информация за променливата. Те са излишни и трябва да бъдат премахнати.

Някои популярни думи за шум са:

  • (Префиксът)
  • Информация
  • Данни
  • Променлива
  • Обект
  • Мениджър

Ако вашият клас се казва UserInfo, можете просто да премахнете информацията и да я направите User. Използването на BookData вместо Book като име на клас е просто невъзможно, тъй като клас съхранява данни във всеки случай.

Можете също да прочетете публикацията в блога на Джеф Атууд за именуването на 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 реда. Колкото по-дълго става една функция, толкова по-вероятно е тя да прави множество неща и да има странични ефекти.

Уверете се, че те просто правят едно нещо

Функциите трябва да правят едно нещо. Те трябва да го направят добре. Те трябва да го правят само. - Чист код

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.

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.