CodeGym /Java блог /Случаен /Правила за кодиране: силата на правилните имена, добрите ...
John Squirrels
Ниво
San Francisco

Правила за кодиране: силата на правилните имена, добрите и лошите коментари

Публикувано в групата
Правила за codeиране: Силата на правилните имена, добри и лоши коментари - 1Колко често ви се е налагало да ровите в codeа на някой друг? Вместо два часа, може да отделите два дни, за да разберете просто логиката на случващото се. Най-смешното е, че за човека, който е написал codeа, всичко е ясно и напълно прозрачно. Това не е изненадващо: в крайна сметка перфектният code е много неясна концепция, защото всеки разработчик има собствена визия за света и codeа. Неведнъж съм попадал в ситуация, когато с колега разглеждаме един и същи code и имахме различни мнения относно неговата коректност и чистота.Правила за codeиране: силата на правилните имена, добри и лоши коментари - 2Звучи познато, нали? Все пак има някои изпитани във времето принципи, които трябва да се спазват. В крайна сметка те ще бъдат изгодни за вас, защото ако оставите codeа си в състоянието, в което вие самите бихте искали да го получите, тогава светът ще стане малко по-щастлив и по-чист. В предишната ни статия(or по-скоро малко ръководство) относно правилата за codeиране, получихме малко усещане за препоръки за писане на система като цяло и нейните съставни части, като обекти, интерфейси, класове, методи и променливи. В същата тази статия небрежно споменах правилното именуване на определени елементи. Бих искал да говоря за това днес, защото правилните имена правят codeа многократно по-лесен за четене. Ще затворим темата за правилния code с някои размишления, малки примери за коментари в codeа и обмисляне дали това е добро or не толкова добро. Е, да започваме.

Правилни имена

Правилните имена подобряват четливостта на codeа, като по този начин намаляват времето, необходимо за запознаване с codeа, тъй като използването на метод е много по-лесно, когато името му грубо описва неговата функционалност. Всичко в codeа се състои от имена (променливи, методи, класове, обекти, файлове и т.н.), така че тази точка става много важна при създаването на правилен, чист code. Въз основа на горното, името трябва да предава meaning, например защо променливата съществува, Howво прави и How се използва. Ще отбележа повече от веднъж, че най-добрият коментар за една променлива е да й дадете добро име.Правила за codeиране: силата на правилните имена, добри и лоши коментари - 3

от сериала "Шерлок" (2010-2017)

Интерфейси за именуване

Интерфейсите обикновено имат имена, които започват с главна буква и се изписват с CamelCase. Когато пишете интерфейс, се смяташе за добра практика да добавите префикса "I", за да го обозначите като интерфейс (например IUserService), но това изглежда доста грозно и разсейващо. В такива случаи е по-добре да пропуснете префикса (UserService) и да добавите "Impl" като суфикс към името на неговата реализация (напр. UserServiceImpl). Или евентуално, в краен случай, добавете префикс "C" към името на изпълнението (напр. CUserService).

Имена на класове

Точно като интерфейсите, имената на класовете са с главни букви и използват CamelCase. Няма meaning дали сме изпequalsи пред зомби апокалипсис, няма meaning дали краят е близо — никога, никога, никога името на клас не трябва да бъде глагол! Имената на класове и обекти трябва да са съществителни or сложни съществителни (UserController, UserDetails, UserAccount и т.н.). Не трябва да поставяте съкращението на приложението в края на името на всеки клас, тъй като това само би добавило ненужна сложност. Например, ако имаме приложение за мигриране на потребителски данни, моля, не добавяйте „UDM“ към всеки клас, т.е. UDMUserDetails, UDMUserAccount, UDMUserController.

Имена на методите

Обикновено имената на методите започват с малка буква, но те също използват камилски регистър (camelCase). По-горе казахме, че имената на класове никога не трябва да бъдат глаголи. Тук ситуацията е точно обратната: имената на методите трябва да са глаголи or глаголни фрази: findUserById, findAllUsers, createUser и т.н. Когато създавате метод (Howто и променливи и класове), използвайте последователна конвенция за именуване, за да избегнете объркване. Например, за да намерите потребител, методът може да бъде наречен getUserById or findUserById. И още нещо: не използвайте хумор в имената на методите, защото другите може да не разберат шегата. В резултат на това те може да не успеят да разберат Howво прави методът.

Имена на променливи

В повечето случаи имената на променливите започват с малка буква и също използват CamelCase, освен когато променливата е глобална константа. В такива случаи всички букви от името се пишат с главни букви и думите се разделят с долна черта ("_"). За удобство можете да използвате смислен контекст, когато именувате променливи. С други думи, когато променлива съществува като част от нещо по-голямо, например име, фамorя or статус. В такива случаи можете да добавите префикс, който указва обекта, към който принадлежи тази променлива. Например: userFirstName, userLastName, userStatus. Трябва също така да избягвате подобни имена на променливи, когато те имат напълно различни значения. Ето някои често срещани антоними, използвани в имена на променливи:
  • начало/край
  • първи последен
  • заключено/отключено
  • мин./макс
  • следващ/предишен
  • стар нов
  • отворен/затворен
  • видим/невидим
  • източник/цел
  • източник/дестинация
  • нагоре надолу

Кратки имена на променливи

Когато имаме променливи като x or n or нещо подобно, ние не виждаме веднага намерението на човека, който е написал codeа. Не е очевидно Howво прави n. Изясняването на това изисква по-внимателно съзерцание (и това означава време, време, време). Да предположим например, че имаме поле, което представлява идентификатора на отговорния потребител. Вместо няHowво име на променлива като x or просто id, ще назовем тази променлива "responsibleUserId", което незабавно подобрява четливостта и информационното съдържание. Въпреки това кратките имена като n имат място като локални променливи в малки методи, където блокът от code, включващ тази променлива, е дълъг само няколко реда, а името на метода идеално описва Howво се случва там. Виждайки такава променлива, разработчикът разбира, че тя е от второстепенно meaning и има много ограничен обхват. В резултат на това обхватът има известна зависимост от дължината на името на променливата: колкото по-дълго е името, толкова по-глобална е променливата и обратно. Като пример, ето метод за намиране на последния запазен потребител по дата:

public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
Тук използваме променливи с кратки имена x и y, за да сортираме потока и след това забравяме за тях.

Оптимална дължина

Нека продължим с темата за дължината на името. Оптималната дължина на името е някъде между n и maximumNumberOfUsersInTheCurrentGroup. С други думи, кратките имена страдат от липса на смисъл, докато имената, които са твърде дълги, удължават програмата, без да добавят четивност, и просто ни мързи да ги пишем всеки път. Освен описания по-горе случай за променливи с кратко име като n, трябва да се придържате към дължина от около 8-16 знака. Това не е строго правило, а само насока.

Малки разлики

Не мога да не спомена фините разлики в имената. Това също е лоша практика, тъй като тези разлики могат просто да бъдат объркващи or да изискват отделяне на много допълнително време, за да ги забележите. Например разликата между InvalidDataAccessApiUsageException и InvalidDataAccessResourceUsageException е трудно да се забележи с един поглед. Често може да възникне объркване при използването на малки букви L и O, защото те лесно могат да бъдат сбъркани с 1 и 0. При някои шрифтове разликата е по-очевидна, при други е по-малко.

Смисъла

Трябва да направим имената смислени, но да не създаваме двусмислие чрез синоними, тъй като например UserData и UserInfo всъщност имат едно и също meaning. В този случай ще трябва да се задълбочим в codeа, за да разберем кой конкретен обект ни трябва. Избягвайте думи, които не предоставят полезна информация. Например в firstNameString защо се нуждаем от думата String? Възможно ли е това наистина да е обект Date? Разбира се, че не. Така че, ние просто използваме firstName. Бих искал също да спомена булевите променливи. Като пример вземете булева стойност с име flagDeleted. Думата знаме няма meaning. По-разумно е да го наречем isDeleted.

Дезинформация

Бих искал също да кажа няколко думи за неправилните конвенции за именуване. Да приемем, че имаме променлива с име userActivityList, но instead of да бъде списък, този обект е няHowъв друг тип контейнер or потребителски обект за съхранение. Това може да обърка средния програмист: по-добре е да го наречем нещо като userActivityGroup or userActivities.

Търсене

Един от недостатъците на кратките и прости имена е, че са трудни за намиране в голямо количество code — Кое би било по-лесно за намиране: „име“ or „NAME_FOR_DEFAULT_USER“? Вторият вариант, разбира се. Трябва да избягваме често срещаните думи (букви) в имената, тъй като те само ще увеличат броя на съвпадащите файлове по време на търсене, което не е добре. Бих искал да ви напомня, че програмистите прекарват повече време в четене на code, отколкото в писане, така че бъдете умни при назоваването на елементите на вашето приложение. Но Howво ще стане, ако добро име просто не може да се намери? Ами ако името на даден метод не описва добре функционалността му? Тук на сцената влизат коментарите.

Коментари

Правила за codeиране: силата на правилните имена, добри и лоши коментари - 4Няма нищо по-добро от уместен коментар, но нищо не претрупва модула като празни, остарели or неверни коментари. Те могат да бъдат нож с две остриета, нали? Все пак не бива да се отнасяте към коментарите като към еднозначно добро, а по-скоро като към по-малкото зло. В края на краищата, коментарът е по същество начин да се компенсира мисленето, което не се изразява ясно в codeа. Например, ние ги използваме, за да предадем по няHowъв начин същността на даден метод, ако самият метод се окаже твърде объркващ. В тази ситуация е по-добре правилно да преработите codeа, отколкото да пишете описателни бележки. Колкото по-стар е коментарът, толкова по-лош е коментарът, защото codeът има тенденция да расте и да се развива, но коментарите може да останат същите. Колкото повече време е минало от създаването на коментар, толкова по-съмнителен може да бъде той. Неточните коментари са много по-лоши от липсата на коментари, защото те са объркващи и измамни, създавайки лъжливи очаквания. И дори ако имаме много труден code, трябва да го пренапишем, instead of да го коментираме.

Видове коментари

  • Правни коментари — Коментари в началото на всеки изходен файл по правни причини, например:

    
    * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
    

  • Информационни коментари — Коментари, представляващи обяснение на codeа (предоставяне на допълнителна информация or разясняване на преднаmeaningто на даден раздел от codeа).

    Например:

    
    /*
    * Combines the user from the database with the one passed for updating
    * When a field in requestUser is empty, it is filled with old data from foundUser
    */
    private User mergeUser(User requestUser, User foundUser) {
           return new User(
           foundUser.getId(),
           requestUser.getFirstName() == null ? requestUser.getFirstName() : foundUser.getFirstName(),
           requestUser.getMiddleName() == null ? requestUser.getMiddleName() : foundUser.getMiddleName(),
           requestUser.getLastName() == null ? requestUser.getLastName() : foundUser.getLastName(),
           requestUser.getAge() == null ? requestUser.getAge() : foundUser.getAge()
           );
           }
    

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

  • Предупредителни коментари — Коментар, предназначен да предупреди други разработчици за нежеланите последици от дадено действие (например, предупреждавайки ги защо даден тест е маркиран като @Ignore):

    
    // Takes too long to run
    // Don't run if you don't have a lot of time
    @Ignore
    @Test
    public void someIntegrationTest() {
           ……
           }
    

  • TODO — Коментари, които са бележка за нещо, което трябва да се направи в бъдеще, но по няHowва причина не може да бъде напequalsо сега. Това е добра практика, но такива коментари трябва да се преглеждат редовно, за да се премахнат неуместните и да се избегне претрупването.

    Пример би бил:

    
    // TODO: Add a check for the current user ID (when the security context is created)
    
    @Override
    public Resource downloadFile(File file) {
           return fileManager.download(file);
           }
    

    Тук отбелязваме факта, че трябва да добавим сравнение на потребителя, извършващ операцията за изтегляне (чийто идентификатор ще извлечем от контекста на сигурността) с този, който е извършил операцията за запазване.

  • Подсилващи коментари — Коментари, подчертаващи важността на обстоятелство, което на пръв поглед може да изглежда незначително.

    Като пример, помислете за част от метод, който запълва тестова база данни с някои скриптове:

    
    Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8)
           .trim()
           .split(";"))
           .forEach(jdbcTemplate::update);
    // The trim() call is very important. It removes possible spaces at the end of the script
    // so that when we read and split into separate requests, we don't end up with empty ones
    

  • Javadoc коментари — Коментари, които описват API за определена функционалност. Има може би най-полезните коментари, тъй като documentираният API е много по-лесен за работа. Въпреки това те също могат да бъдат остарели като всеки друг вид коментар. Така че, никога не забравяйте, че основният принос към documentацията се прави не от коментарите, а от добрия code.

    Ето пример за доста често срещан метод за актуализиране на потребител:

    
    /**
    * Updates the passed fields for a user based on its id.
         *
    * @param id id of the user to be updated
    * @param user user with populated fields for updating
    * @return updated user
    */
           User update(Long id, User user);
    

Лоши коментари

  • мърморещ коментар — Коментари, които обикновено се пишат набързо и чийто смисъл е разбираем само за разработчика, който ги е написал, тъй като само той or тя възприема нюансираната ситуация, за която се отнася коментарът.

    Помислете за този пример:

    
    public void configureSomeSystem() {
           try{
           String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE);
           FileInputStream stream = new FileInputStream(configPath);
           } catch (FileNotFoundException e) {
           // If there is no configuration file, the default configuration is loaded 
          }
    }
    

    Кой зарежда тези настройки? Заредени ли са вече? Този метод трябва ли да прихваща изключения и да зарежда настройките по подразбиране? Възникват твърде много въпроси, на които може да се отговори само чрез проучване на други части на системата.

  • Излишни коментари — Коментари, които не носят ниHowво семантично натоварване, тъй като това, което се случва в даден раздел от codeа, е пределно ясно. С други думи, коментарът не се чете по-лесно от codeа.

    Да видим пример:

    
    public class JdbcConnection{
    public class JdbcConnection{
       /**
        * The logger associated with the current class
        */
       private Logger log = Logger.getLogger(JdbcConnection.class.getName());
    
       /**
        * Creates and returns a connection using the input parameters
        */
       public static Connection buildConnection(String url, String login, String password, String driver) throws Exception {
           Class.forName(driver);
           connection = DriverManager.getConnection(url, login, password);
           log.info("Created connection with db");
           return connection;
       }
    

    Какъв е смисълът от подобни коментари? Всичко, което обясняват, вече е напълно ясно.

  • Ненадеждни коментари — Коментари, които са неверни и само подвеждащи (дезинформация). Например, ето един.

    
    /**
    * Helper method. Closes the connection with the scanner if isNotUsing is true
    */
    private void scanClose(Scanner scan, boolean isNotUsing) throws Exception {
       if (!isNotUsing) {
           throw new Exception("The scanner is still in use");
       } scan.close();
    }
    

    Какво не е наред с този коментар? Фактът, че малко ни лъже, че връзката е затворена, ако isNotUsing е невярно, а не обратното, Howто ни информира коментарът.

  • Задължителни коментари — Коментари, които се считат за задължителни (напр. Javadoc коментари), но които всъщност понякога се натрупват прекомерно и са ненадеждни и ненужни (трябва да помислите дали тези коментари наистина са необходими).

  • Пример:

    
    /**
    * Create a user based on the parameters
    * @param firstName first name of the created user
    * @param middleName middle name of the created user
    * @param lastName last name of the created user
    * @param age age of the created user
    * @param address address of the created user
    * @return user that was created
    */
    User createNewUser(String firstName, String middleName, String lastName, String age, String address);
    

    Бихте ли могли да разберете Howво прави методът без тези коментари? Най-вероятно да, така че коментарите стават безсмислени тук.

  • Коментари в журнала — Коментари, които понякога се добавят в началото на модул всеки път, когато се редактира (нещо като регистър на промените).

    
    /**
    * Records kept since January 9, 2020;
    **********************************************************************
    * 9 Jan 2020: Providing a database connection using JDBC Connection;
    * 15 Jan 2020: Adding DAO-level interfaces for working with the database;
    * 23 Jan 2020: Adding integration tests for the database;
    * 28 Jan 2020: Implementation of DAO-level interfaces;
    * 1 Feb 2020: Development of interfaces for services,
    * in accordance with the requirements specified in user stories;
    * 16 Feb 2020: Implementation of service interfaces
    * (implementation of business logic related to the work of the database);
    * 25 Feb 2020: Adding tests for services;
    * 8 Mar 2020: Celebration of International Women's Day (Terry is drunk again);
    * 21 Mar 2020: Refactoring the service layer;
    */
    

    Някога този подход беше оправдан, но с появата на системите за контрол на версиите (например Git), той се превърна в ненужно претрупване и усложняване на codeа.

  • Authorски коментари — Коментари, чиято цел е да посочат лицето, което е написало codeа, за да можете да се свържете с него/нея и да обсъдите How, Howво и защо, напр.

    
    * @author Bender Bending
    

    Още веднъж системите за контрол на версиите помнят точно кой е добавил няHowъв code и кога, така че този подход е излишен.

  • Коментиран code — Код, който е коментиран по една or друга причина. Това е един от най-лошите навици, защото това, което се случва, е да коментирате нещо и да го забравите, а след това другите разработчици просто нямат смелостта да го изтрият (в края на краищата, Howво ще стане, ако е нещо ценно?).

    
    //    public void someMethod(SomeObject obj) {
    //    .....
    //    }
    

    В резултат на това коментираният code се натрупва като боклук. В ниHowъв случай не трябва да оставяте такъв code. Ако наистина имате нужда от нея, не забравяйте за системата за контрол на версиите.

  • Неочевидни коментари — Коментари, които описват нещо по прекалено сложен начин.

    
    /*
        * Start with an array large enough to store
        * all the data bytes (plus filter bytes) with a cushion, plus 300 bytes
        * for header data
        */
    this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];
    

    Коментарът трябва да обяснява codeа. То само по себе си не трябва да се нуждае от обяснение. И така, Howво не е наред тук? Какво представляват "филтърните byteове"? Какво означава това "+ 1"? Защо точно 300?

Ако вече сте решor да пишете коментари, ето няколко съвета:
  1. Използвайте стилове, които са лесни за поддръжка: поддържането на стилове, които са твърде изискани и екзотични, е досадно и отнема много време.
  2. Не използвайте коментари в края на реда, които се отнасят до единични редове: резултатът е голяма купчина коментари. Нещо повече, трудно е да се измисли смислен коментар за всеки ред.
  3. Когато пишете коментар, опитайте се да отговорите на въпроса „защо“, а не „How“.
  4. Избягвайте съкратена информация. Както казах по-горе, нямаме нужда от обяснение за коментар: самият коментар е обяснението.
  5. Можете да използвате коментари, за да отбелязвате единици и стойностни диапазони.
  6. Поставете коментарите близо до codeа, който описват.
И накрая, все пак искам да ви напомня, че най-добрият коментар е без коментар, а по-скоро използването на умело наименуване във вашето приложение. Като правило през повечето време ще работим със съществуващ code, като го поддържаме и разширяваме. Много по-удобно е, когато този code е лесен за четене и разбираем, тъй като лошият code е пречка. Това е като да хвърлите гаечен ключ в работата и бързането е негов верен спътник. И колкото повече лош code имаме, толкова повече пада производителността. Това означава, че трябва да преработваме от време на време. Но ако от самото начало се опитате да напишете code, който няма да накара следващите разработчици да искат да ви намерят и убият, тогава няма да е необходимо да го преработвате толкова често. Но все пак ще е необходимо, тъй като условията и изискванията на продукта постоянно се променят с добавянето на нови зависимости и връзки. Е, предполагам, че това е всичко за мен днес. Благодаря на всички, които прочетоха дотук :)
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION