JavaRush /Blog Java /Random-PL /Zasady kodeksu: siła prawidłowego nazewnictwa, dobre i zł...

Zasady kodeksu: siła prawidłowego nazewnictwa, dobre i złe komentarze

Opublikowano w grupie Random-PL
Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 1 Jak często musiałeś rozumieć cudzy kod? Kiedy zamiast kilku godzin spędzasz dni, aby zrozumieć logikę tego, co się dzieje. Zabawne jest to, że dla osoby, która napisała ten kod, wszystko jest jasne i bardzo przejrzyste. I nie jest to zaskakujące: w końcu doskonały lub idealny kod to bardzo niejasne pojęcie, ponieważ każdy programista ma odpowiednio własną wizję świata i kodu. Nie raz spotkałem się z sytuacją, gdy z kolegą patrzyliśmy na ten sam kod i mieliśmy różne opinie na temat jego poprawności i czystości. Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 2To znajome uczucie, prawda? Są jednak pewne sprawdzone przez czas punkty, których należy się trzymać, co ostatecznie zadziała na naszą korzyść, ponieważ jeśli zostawisz swój kod w takim stanie, w jakim sam chciałbyś go otrzymać, świat byłby trochę szczęśliwszy i odkurzacz. Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 3W naszym ostatnim artykule o zasadach pisania kodu (a raczej małym poradniku) poruszyliśmy trochę zalecenia dotyczące pisania systemu jako całości i jego elementów takich jak obiekty, ich interfejsy, klasy, metody i zmienne. Tam pokrótce wspomniałem o poprawnym nazewnictwie poszczególnych elementów. Dziś chciałbym poruszyć właśnie ten temat, gdyż poprawne nazwy znacznie ułatwiają czytelność kodu. Temat poprawnego kodu zamkniemy za pomocą refleksji i małych przykładów komentarzy w kodzie - czy jest dobry, czy nie. Więc zacznijmy.

Poprawne nazewnictwo

Prawidłowe nazwy poprawiają czytelność kodu, oszczędzając tym samym czas na zapoznawaniu się, ponieważ znacznie łatwiej jest zastosować metodę, gdy nazwa z grubsza opisuje jej funkcjonalność. Ponieważ wszystko w kodzie składa się z nazw (zmiennych, metod, klas, obiektów plików itp.), ten punkt staje się bardzo ważny podczas tworzenia poprawnego, czystego kodu. W związku z powyższym nazwa powinna przekazywać znaczenie, dlaczego na przykład istnieje zmienna, co robi i jak jest używana. Będę stale podkreślał, że najlepszym komentarzem do opisu zmiennej jest jej poprawna nazwa. Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 4

Nazewnictwo interfejsów

Interfejsy zazwyczaj używają nazw rozpoczynających się z dużej litery i pisanych wielką literą (CamelCase). Kiedyś dobrą praktyką przy pisaniu interfejsu było poprzedzanie go literą I w celu oznaczenia go jako interfejsu (np. IUserService), ale jest to dość brzydkie i rozpraszające. W takich przypadkach lepiej pisać bez niego (UserService), a do jego implementacji dodać -Impl (UserServiceImpl). Cóż, lub w ostateczności dodaj przedrostek C (CUserService) do jego implementacji.

Nazwy klas

Podobnie jak interfejsy, nazwy są pisane wielką literą i mają styl wielbłąda (CamelCase). Nieważne, jaki rodzaj apokalipsy ma miejsce, nieważne, jak szybkie są terminy, ale nigdy, pamiętaj, nazwa zajęć nie powinna być czasownikiem! Nazwy klas i obiektów muszą być rzeczownikami i ich kombinacjami (UserController, UserDetails, UserAccount itd.). Nie należy podawać nazwy każdej klasy ze skrótem tej aplikacji, bo to tylko zwiększy niepotrzebną komplikację (przykładowo mamy aplikację User Data Migration i do każdej klasy dodamy UDM - UDMUserDeatils, UDMUserAccount, UDMUserController ).

Nazwy metod

Zwykle nazwy metod zaczynają się od małej litery, ale używają też stylu wielbłądziego (CamelCase). Powyżej rozmawialiśmy o tym, że nazwy klas nigdy nie powinny być czasownikami. Tutaj sytuacja jest diametralnie odwrotna: nazwy metod muszą być czasownikami lub ich kombinacjami z czasownikami: findUserById, findAllUsers, createUser i tak dalej. Tworząc metodę (a także zmienne i klasy), aby uniknąć nieporozumień, należy stosować jedno podejście do nazewnictwa. Na przykład, aby znaleźć użytkownika, metodę można zapisać jako getUserById lub findUserById. I jeszcze jedno: nie używajcie humoru w nazwach metod, bo mogą nie zrozumieć żartu, a także tego, co ta metoda robi.

Nazwy zmiennych

W większości przypadków nazwy zmiennych zaczynają się od małej litery i zawierają także litery Camelcase, z wyjątkiem przypadków, gdy zmienna jest stałą globalną. W takim przypadku wszystkie litery nazwiska są pisane wielkimi literami, a słowa oddzielane są znakiem podkreślenia - „_”. Nazywając zmienne, możesz dla wygody użyć znaczącego kontekstu. Innymi słowy, gdy zmienna jest częścią czegoś większego - na przykład imię, nazwisko, status - w takim przypadku można dodać przedrostek wskazujący obiekt, którego częścią jest dana zmienna. Na przykład: imię użytkownika, nazwisko użytkownika, stan użytkownika. Należy także unikać podobnych nazw zmiennych, gdy mają one zupełnie inne znaczenie. Najczęstsze antonimy zmiennych:
  • początek/koniec
  • pierwszy/ostatni
  • zablokowane/odblokowane
  • minimum maksimum
  • Następna poprzednia
  • stare nowe
  • otwarty/zamknięty
  • widoczny/niewidoczny
  • cel źródłowy
  • źródło/miejsce docelowe
  • Góra dół

Krótkie nazwy zmiennych

Kiedy mamy zmienne takie jak x, n lub coś w tym rodzaju, nie od razu widzimy intencje osoby, która napisała kod. Nie jest oczywiste, co robi metoda n: wymaga bardziej przemyślanego myślenia (a to jest czas, czas, czas). Przykładowo mamy pole - id odpowiedzialnego użytkownika i zamiast jakiejś nazwy typu x czy po prostu id nazwiemy tę zmienną ResponsibleUserId, co od razu zwiększa czytelność i wymowę. Jednak krótkie nazwy, takie jak n, mają swoje miejsce jako lokalne zmiany małych metod, gdzie blok kodu z tą zmianą to tylko kilka linii kodu, a nazwa metody doskonale opisuje, co się tam dzieje. Deweloper widząc taką zmienną zdaje sobie sprawę z jej drugorzędnego znaczenia i bardzo ograniczonego zakresu. W rezultacie istnieje pewna zależność od długości nazwy zmiennej: im dłuższa, tym bardziej globalna jest zmienna i odwrotnie. Przykładowa metoda wyszukiwania ostatniego zapisanego użytkownika według daty:
public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("Any user doesn't exist "));
}
Tutaj używamy krótkich nazw x i y, aby ustawić sortowanie strumienia i zapominamy o nich.

Optymalna długość

Kontynuujmy temat długości imienia. Optymalna długość nazwy mieści się pomiędzy długością nazwy MaximumNumberOfUsersInTheCurrentGroup a n. Oznacza to, że zbyt krótkie cierpią na brak znaczenia, a zbyt długie rozciągają program bez zwiększenia czytelności i są po prostu zbyt leniwi, aby je za każdym razem pisać. Nie biorąc pod uwagę powyższego przypadku, dla zmiennych o krótkiej nazwie, np. n, należy zachować długość około 8-16 znaków. To nie jest ścisła zasada: raczej wytyczna.

Małe różnice

Nie mogę ignorować subtelnych różnic w nazwach, ponieważ jest to również zła praktyka, ponieważ możesz po prostu się pomylić lub spędzić dużo dodatkowego czasu na zauważaniu drobnych różnic w nazwach. Na przykład różnica między InvalidDataAccessApiUsageException i InvalidDataAccessResourceUsageException jest trudna do zauważenia na pierwszy rzut oka. Ponadto przy użyciu małych L i O często mogą pojawić się dezinformacje, ponieważ można je łatwo pomylić z 1 i 0: w niektórych czcionkach różnica jest bardziej oczywista, w innych mniej.

Część semantyczna

Musimy umieścić część semantyczną w nazwach, ale nie przesadzajmy z synonimami, ponieważ na przykład UserData i UserInfo w rzeczywistości mają to samo znaczenie i będziemy musieli pogrzebać trochę głębiej w kodzie, aby zrozumieć, jakiego konkretnego obiektu potrzebujemy . Unikaj słów nieinformujących, na przykład FirstNameString: po co nam ciąg słów? Czy nazwa może być obiektem typu data? Oczywiście, że nie: dlatego po prostu – imię. Jako przykład chciałbym wymienić zmienne boolowskie, na przykład flagDelete. Słowo flaga nie ma żadnego znaczenia semantycznego. Rozsądniej byłoby nazwać to - isDelete.

Dezinformacja

Chciałbym również powiedzieć kilka słów o błędnym nazewnictwie. Załóżmy, że mamy nazwę userActivityList, a obiekt o takiej nazwie nie jest typu List, ale jest to inny kontener lub niestandardowy obiekt do przechowywania. Może to zmylić przeciętnego programistę: lepiej byłoby nazwać to czymś w rodzaju userActivityGroup lub userActivities.

Szukaj

Jedną z wad krótkich i prostych nazw jest to, że trudno je znaleźć w dużej ilości kodu, bo co byłoby łatwiej znaleźć: zmienną o nazwie nazwa czy NAME_FOR_DEFAULT_USER? Oczywiście druga opcja. Należy unikać często występujących w nazwach słów (liter), gdyż spowoduje to jedynie zwiększenie liczby plików znalezionych podczas wyszukiwania, co nie jest dobre. Przypominamy, że programiści spędzają więcej czasu na czytaniu kodu niż na jego pisaniu, dlatego pamiętaj o nazewnictwie elementów swojej aplikacji. Ale co, jeśli nie udało Ci się go nazwać pomyślnie? Co jeśli nazwa metody nie opisuje dobrze jej funkcjonalności? Tutaj właśnie wchodzi to w grę, naszym kolejnym punktem są komentarze.

Uwagi

Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 5Nie ma nic lepszego niż odpowiedni komentarz, ale nic tak nie zaśmieca modułu jak komentarze bez znaczenia, nieaktualne lub wprowadzające w błąd. To miecz obosieczny, prawda? Nie należy jednak traktować komentarzy jako jednoznacznego dobra, a raczej jako mniejsze zło. Przecież komentarz w swej istocie jest rekompensatą za bezskutecznie wyrażoną myśl w kodzie. Używamy ich na przykład, aby w jakiś sposób przekazać istotę metody, jeśli okaże się ona zbyt zagmatwana. W takiej sytuacji lepiej jest poprawnie refaktoryzować kod, niż pisać notatki opisowe. Im starszy komentarz, tym gorzej, ponieważ kod ma tendencję do rozrastania się i ewolucji, ale komentarz może pozostać taki sam, a im dalej idzie, tym bardziej wątpliwe stają się te notatki. Niedokładne komentarze są znacznie gorsze niż brak komentarzy, ponieważ wprowadzają w błąd i oszukują, dając fałszywe oczekiwania. I nawet jeśli mamy bardzo skomplikowany kod, nadal warto go nie komentować, tylko przepisać.

Rodzaje komentarzy

  • komentarze prawne to komentarze pozostawione na początku każdego pliku kodu źródłowego ze względów prawnych, takich jak:

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

  • komentarze informacyjne - komentarze wyjaśniające kod (podające dodatkowe informacje lub przeznaczenie danej sekcji kodu).

    Jako przykład:

    /*
    * Объединяет пользователя из бд и пришедшего для обновления
    * Когда в requestUser поле пустое, оно заполняется старыми данными из 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()
           );
           }

    W tym przypadku można obejść się bez komentarzy, gdyż nazwa metody i jej argumenty w połączeniu z bardzo przejrzystą funkcjonalnością opisują się całkiem nieźle.

  • komentarz ostrzegawczy - komentarz, którego celem jest ostrzeżenie innych programistów o niepożądanych konsekwencjach jakiegoś działania (np. dlaczego test został oznaczony jako @Ignore):

    // Слишком долго отрабатывает
    // Не запускайте, если не располагаете избытком времени
    @Ignore
    @Test
    public void someIntegrationTest() {
           ……
           }
  • TODO - komentarze będące notatkami na przyszłość, które trzeba będzie zrobić, ale z jakiegoś powodu nie można tego zrobić teraz. Jest to dobra praktyka, ale nadal należy je regularnie przeglądać, aby usunąć nieistotne i uniknąć bałaganu.

    Примером послужит:

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

    Тут мы помечаем, что нужно добавить проверку юзера, который скачивает (id которого мы вытащим из security контекста) с тем, кто сохранил.

  • усLubвающий комментарий — комментарий, подчеркивающий важность Jakого-то обстоятельства, что на первый взгляд может показаться несущественным.

    Как пример, кусочек метода, заполняющий тестовую БД, некими скриптами:

    Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8)
           .trim()
           .split(";"))
           .forEach(jdbcTemplate::update);
    // Вызов trim() очень важен, убирает возможные пробелы в конце скрипта
    // чтобы при считке и разбивке на отдельные wniosekы не было пустых

  • javaDoc — комментарии, которые описывают API определенного функционала для общего пользования. Наверное, самые полезные комментарии, так Jak с dokumentированным API в разы легче работать, но они также могут устаревать, Jak и любые другие. Поэтому не забываем, что главный вклад в dokumentацию вносится не комментариями, а хорошим kodом.

    Пример вполне обычного метода обновления пользователя:

    /**
    * Обновляет передаваемые поля для пользователя по id.
    *
    * @param id  id обновляемого пользователя
    * @param user пользователь с заполненными полями для обновления
    * @return обновленный пользователь
    */
           User update(Long id, User user);

Плохие сценарии комментариев

Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 7
  • бормочущий комментарий — комментарии, которые обычно пишут на скорую руку, смысл которых понятен только разработчику, писавшего их, так Jak только он видит ту ситуацию с теми нюансами, на которые он и ссылается.

    Рассмотрим данный пример:

    public void configureSomeSystem() {
           try{
           String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE);
           FileInputStream stream = new FileInputStream(configPath);
           }  catch (FileNotFoundException e) {
           //В случае отсутствия конфигурационного plik, загружается конфигурация по умолчанию
          }
    }

    Кто загружает эти настройки? Были ли они загружены ранее? Метод предназначен для перехвата исключений и вызова дефолтных настроек? Слишком много вопросов возникает, ответы на которые можно получить лишь углубившись в изучение других частей системы.

  • избыточный комментарий — комментарий, который не несёт смысловой нагрузки, так Jak и так понятно что происходит в заданном участке kodа (он читается не проще, чем kod).

    Смотрим пример:

    public class JdbcConnection{
    public class JdbcConnection{
       /**
        * Журнальный компонент, связанный с текущим классом
        */
       private Logger log = Logger.getLogger(JdbcConnection.class.getName());
    
       /**
        * Создаёт и возвращает connection с помощью входящих параметров
        */
       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;
       }

    Какой смысл таких комментариев, если мы и так всё прекрасно видим

  • недостоверные комментарии — комментарии, не соответствующие истине и лишь вгоняющие в заблуждение (дезинформирующие). Как например:

    /**
    * Вспомогательный метод, закрывает соединение со сканером, если isNotUsing истинно
    */
    private void scanClose(Scanner scan, boolean isNotUsing) throws Exception {
       if (!isNotUsing) {
           throw new Exception("The scanner is still in use");
       } scan.close();
    }

    Co в этом комменте не так? А то, что он немножко врёт нам, ведь соединение закрывается, если isNotUsing = false, но ниJak не наоборот, Jak нам вещает пометка.

  • обязательные комментарии — комментарии, которые считают обязательными (Javadoc), но кои по факту иногда бывают излишне нагромождающими, недостоверными и ненужными (нужно задуматься, а нужны ли здесь такие комментарии).

    Пример:

    /**
    *  kreacja пользователя по переданным параметрам
    * @param firstName Nazwa созданного пользователя
    * @param middleName среднее Nazwa созданного пользователя
    * @param lastName фамLubя созданного пользователя
    * @param age возраст созданного пользователя
    * @param address adresс созданного пользователя
    * @return пользователь который был создан
    */
    User createNewUser(String firstName, String middleName, String lastName, String age, String address);

    Смогли бы вы понять, что делает метод без этих комментариев? Скорее всего да, поэтому комментарии в этом случае стают бессмысленными.

  • журнальные комментарии — комментарии, которые иногда добавляют в начало модуля, при каждом его редактировании (что-то вроде журнала вносимых изменений).

    /**
    *  Записи ведутся с 09 января 2020;
    **********************************************************************
    *  09.01.2020  : Обеспечение соединения с БД с помощью Jdbc Connection;
    *  15.01.2020  : Добавление интерфейсов уровня дао для работы с БД;
    *  23.01.2020  : Добавление интеграционных тестов для БД;
    *  28.01.2020  : Имплементация интерфейсов уровня дао;
    *  01.02.2020  : Разработка интерфейсов для сервисов,
    *  согласно требованиям прописанным в user stories;
    *  16.02.2020  : Имплементация интерфейсов сервисов
    *  (реализация бизнес логики связанной с работой БД);
    *  25.02.2020  : Добавление тестов для сервисов;
    *  08.03.2020  : Празднование восьмого марта(Миша опять в хлам);
    *  21.03.2020  : Рефакторинг сервис слоя;
    */

    Когда-то этот проход был оправдан, но с появлением систем управления исходным kodом (например — Git), это стало лишним нагромождением и усложнением kodа.

  • комментарии ссылки на авторов — комментарии, преднаoznaczającyм которых является, указание человека, писавшего kod, чтобы можно было связаться и обсудить, Jak что и зачем:

    * @author  Bender Benderovich

    Опять же, системы контроля версий прекрасно запоминают, кто и когда добавил данный kod, и подобный подход излишен.

  • skomentowany kod to kod, który został skomentowany z tego czy innego powodu. Jeden z najgorszych nawyków, bo sam to skomentowałeś i zapomniałeś, a inni programiści po prostu nie mają odwagi go usunąć (a co jeśli jest to coś wartościowego).

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

    W rezultacie gromadzi się jak śmieci. W żadnym wypadku nie należy pozostawiać takiego kodu. Jeśli naprawdę tego potrzebujesz, nie zapomnij o systemie kontroli wersji.

  • komentarze nieoczywiste to komentarze, które opisują coś w niepotrzebnie skomplikowany sposób.

    /*
        * Начать с массива, размер которого достаточен для хранения
        * всех bajtов данных (плюс bajtы фильтра) с запасом, плюс 300 bajt
        * для данных заголовка
        */
    this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];

    Komentarz powinien wyjaśniać kod, a nie sam w sobie. Co jest tutaj? Co to są „bajty filtrujące”? Co ma z tym wspólnego +1? Dlaczego dokładnie 300?

Jeśli zdecydujesz się pisać komentarze, oto kilka wskazówek, jak z nich korzystać:
  1. Używaj stylów, które są łatwe w utrzymaniu: utrzymywanie stylów, które są zbyt fantazyjne i egzotyczne, może być denerwujące i czasochłonne.
  2. Nie używaj komentarzy na końcu wierszy, które odnoszą się do pojedynczych wierszy: tworzy to duży stos komentarzy i trudno jest wymyślić wyrazisty komentarz do każdego wiersza.
  3. Tworząc komentarz, staraj się odpowiedzieć na pytanie „dlaczego”, a nie „jak”.
  4. Unikaj skrótów. Jak powiedziałem powyżej, nie potrzebujemy wyjaśnienia komentarza: komentarz jest wyjaśnieniem.
  5. W komentarzach można zaznaczyć jednostki miary i zakres dopuszczalnych wartości.
  6. Umieść komentarze blisko kodu, który opisują.
W związku z tym nadal przypominam: najlepsze komentarze to brak komentarza, a zamiast niego odpowiednie nazewnictwo w aplikacji. Z reguły przez większość czasu będziemy już pracować z gotowym kodem, utrzymując go i rozwijając. Jest o wiele wygodniej, gdy ten kod jest łatwy do odczytania i zrozumienia, ponieważ zły kod przeszkadza, utrudnia pracę, a pośpiech jest jego wiernym towarzyszem. Im więcej mamy złego kodu, tym bardziej spada wydajność, dlatego od czasu do czasu musimy przeprowadzić refaktoryzację. Jeśli jednak od samego początku będziesz próbował pisać kod, dla którego kolejni programiści nie będą chcieli Cię znaleźć i zabić, to rzadziej będziesz musiał go refaktoryzować. Ale nadal będzie to konieczne, ponieważ warunki i wymagania dotyczące produktu stale się zmieniają, uzupełniane przez dodawanie dodatkowych połączeń, i nie ma od tego ucieczki. Na koniec zostawiam Wam kilka ciekawych linków do zapoznania się z tym tematem tutaj , tutaj i tutaj . To chyba tyle na dzisiaj, dziękuję wszystkim, którzy przeczytali)) Zasady pisania kodu: siła poprawnego nazewnictwa, dobre i złe komentarze - 8
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION