Reguli de codificare: puterea numelor corecte, a comentariilor bune și rele - 1Cât de des ai fost nevoit să sapi în codul altcuiva? În loc de două ore, puteți petrece două zile pentru a înțelege pur și simplu logica a ceea ce se întâmplă. Lucrul amuzant este că pentru persoana care a scris codul, totul este clar și complet transparent. Acest lucru nu este surprinzător: la urma urmei, codul perfect este un concept foarte vag, deoarece fiecare dezvoltator are propria viziune asupra lumii și, de asemenea, a codului. De mai multe ori am fost într-o situație în care un coleg și cu mine ne-am uitat la același cod și am avut păreri diferite despre corectitudinea și curățenia lui.Reguli de codificare: puterea numelor corecte, a comentariilor bune și rele - 2Sună cunoscut, nu-i așa? Cu toate acestea, există câteva principii testate în timp care ar trebui respectate. Până la urmă, îți vor fi avantajoase, pentru că dacă îți lași codul în starea în care tu însuți ai vrea să-l primești, atunci lumea ar deveni puțin mai fericită și mai curată. În articolul nostru anterior(sau mai degrabă, un mic ghid) despre regulile de codificare, am primit un pic de recomandări pentru scrierea unui sistem ca întreg și a părților sale constitutive, cum ar fi obiecte, interfețe, clase, metode și variabile. În același articol, am menționat întâmplător denumirea corectă a anumitor elemente. Aș vrea să vorbesc despre asta astăzi, deoarece numele corecte fac codul de multe ori mai ușor de citit. Vom închide subiectul codului corect cu câteva reflecții, mici exemple de comentarii în cod și luând în considerare dacă acest lucru este bun sau nu atât de bun. Ei bine, să începem.

Nume corecte

Numele corecte îmbunătățesc lizibilitatea codului, reducând astfel timpul necesar pentru a vă familiariza cu codul, deoarece utilizarea unei metode este mult mai ușoară atunci când numele acesteia descrie aproximativ funcționalitatea acesteia. Totul în cod constă din nume (variabile, metode, clase, obiecte, fișiere etc.), așa că acest punct devine foarte important atunci când se creează cod corect și curat. Pe baza celor de mai sus, numele ar trebui să transmită sens, de exemplu, de ce există variabila, ce face și cum este utilizată. Voi observa de mai multe ori că cel mai bun comentariu pentru o variabilă este să îi dai un nume bun.Reguli de codificare: puterea numelor corecte, a comentariilor bune și rele - 3

din serialul TV „Sherlock” (2010-2017)

Denumirea interfețelor

Interfețele au de obicei nume care încep cu o literă majusculă și sunt scrise în CamelCase. Când scrii o interfață, se considera o practică bună să adaugi prefixul „I” pentru a o desemna ca interfață (de exemplu, IUserService), dar asta pare destul de urât și distrage atenția. În astfel de cazuri, este mai bine să omiteți prefixul (UserService) și să adăugați „Impl” ca sufix la numele implementării sale (de ex. UserServiceImpl). Sau, eventual, ca ultimă soluție, adăugați un prefix „C” la numele implementării (ex. CUserService).

Numele claselor

La fel ca interfețele, numele claselor sunt scrise cu majuscule și folosesc CamelCase. Nu contează dacă ne confruntăm cu o apocalipsă zombie, nu contează dacă sfârșitul este aproape - niciodată, niciodată, niciodată numele unei clase nu ar trebui să fie un verb! Numele de clase și de obiecte trebuie să fie substantive sau substantive compuse (UserController, UserDetails, UserAccount și așa mai departe). Nu ar trebui să puneți abrevierea aplicației la sfârșitul numelui fiecărei clase, deoarece asta ar adăuga doar complexitate inutilă. De exemplu, dacă avem o aplicație de migrare a datelor utilizatorului, atunci vă rugăm să nu adăugați „UDM” la fiecare clasă, adică UDMUserDetails, UDMUserAccount, UDMUserController.

Nume de metode

De obicei, numele metodelor încep cu o literă mică, dar folosesc și stilul majuscule camel (camelCase). Mai sus, am spus că numele claselor nu ar trebui să fie niciodată verbe. Aici situația este exact invers: numele metodelor ar trebui să fie verbe sau expresii verbale: findUserById, findAllUsers, createUser și așa mai departe. Când creați o metodă (precum și variabile și clase), utilizați o convenție de denumire consecventă pentru a evita confuzia. De exemplu, pentru a găsi un utilizator, o metodă ar putea fi numită getUserById sau findUserById. Și încă ceva: nu folosi umorul în denumirile metodelor, pentru că alții s-ar putea să nu înțeleagă gluma. Drept urmare, ei pot să nu înțeleagă ce face metoda.

Nume de variabile

În cele mai multe cazuri, numele variabilelor încep cu o literă mică și, de asemenea, folosesc camelCase, cu excepția cazului în care variabila este o constantă globală. În astfel de cazuri, toate literele numelui sunt scrise cu majuscule, iar cuvintele sunt separate printr-o liniuță de subliniere ("_"). Pentru comoditate, puteți utiliza context semnificativ atunci când denumiți variabile. Cu alte cuvinte, atunci când o variabilă există ca parte a ceva mai mare, de exemplu, prenume, prenume sau stare. În astfel de cazuri, puteți adăuga un prefix care indică obiectul căruia îi aparține această variabilă. De exemplu: userFirstName, userLastName, userStatus. De asemenea, ar trebui să evitați nume similare pentru variabile atunci când acestea au semnificații complet diferite. Iată câteva antonime frecvent întâlnite folosite în numele variabilelor:
  • începe/termină
  • primul Ultimul
  • blocat/deblocat
  • minim maxim
  • următorul/anterior
  • vechi nou
  • deschis/inchis
  • vizibil/invizibil
  • sursă/țintă
  • sursa/destinatia
  • sus jos

Nume scurte de variabile

Când avem variabile precum x sau n sau ceva de genul acesta, nu vedem imediat intenția persoanei care a scris codul. Nu este evident ce face n. Înțelegerea asta necesită o contemplare mai atentă (și asta înseamnă timp, timp, timp). De exemplu, să presupunem că avem un câmp care reprezintă id-ul utilizatorului responsabil. În loc de un nume de variabilă precum x sau pur și simplu id, vom numi această variabilă „responsibleUserId”, ceea ce îmbunătățește imediat lizibilitatea și conținutul de informații. Acestea fiind spuse, nume scurte precum n au un loc ca variabile locale în metodele mici, unde blocul de cod care implică această variabilă are doar câteva rânduri, iar numele metodei descrie perfect ceea ce se întâmplă acolo. Văzând o astfel de variabilă, un dezvoltator înțelege că are o importanță secundară și are un domeniu de aplicare foarte limitat. Drept urmare, domeniul de aplicare are o anumită dependență de lungimea numelui unei variabile: cu cât numele este mai lung, cu atât variabila este mai globală și invers. De exemplu, iată o metodă pentru a găsi ultimul utilizator salvat după dată:

public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
Aici folosim variabilele cu nume scurt x și y pentru a sorta fluxul și apoi uităm de ele.

Lungime optimă

Să continuăm cu subiectul lungimii numelui. Lungimea optimă a numelui este undeva între n și maximumNumberOfUsersInTheCurrentGroup. Cu alte cuvinte, numele scurte suferă de o lipsă de sens, în timp ce numele prea lungi prelungesc programul fără a adăuga lizibilitate și pur și simplu ne este prea lene să le scriem de fiecare dată. În afară de cazul descris mai sus pentru variabilele cu un nume scurt precum n, ar trebui să rămâneți la o lungime de aproximativ 8-16 caractere. Aceasta nu este o regulă strictă, ci doar un ghid.

Mici diferențe

Nu pot să nu menționez diferențe subtile de nume. Aceasta este, de asemenea, o practică proastă, deoarece aceste diferențe pot fi pur și simplu confuze sau necesită a petrece mult timp suplimentar pentru a le observa. De exemplu, diferența dintre InvalidDataAccessApiUsageException și InvalidDataAccessResourceUsageException este dificil de observat dintr-o privire. Confuzia poate apărea adesea și atunci când se utilizează litere mici L și O, deoarece acestea pot fi ușor confundate cu 1 și 0. În unele fonturi diferența este mai evidentă, în unele este mai mică.

Sensul

Trebuie să facem numele semnificative, dar să nu creăm ambiguitate prin sinonime, deoarece, de exemplu, UserData și UserInfo au de fapt același sens. În acest caz, ar trebui să săpăm mai adânc în cod pentru a înțelege ce obiect anume avem nevoie. Evitați cuvintele care nu transmit informații utile. De exemplu, în firstNameString, de ce avem nevoie de cuvântul String? Ar putea fi acesta cu adevărat un obiect Date? Desigur că nu. Deci, pur și simplu folosim firstName. Aș dori să menționez și variabile booleene. Ca exemplu, luați un boolean numit flagDeleted. Cuvântul steag nu are sens. Este mai rezonabil să-l numești isDeleted.

Dezinformare

De asemenea, aș dori să spun câteva cuvinte despre convențiile incorecte de denumire. Să presupunem că avem o variabilă numită userActivityList, dar în loc să fie o Listă, acest obiect este un alt tip de container sau un obiect de stocare personalizat. Acest lucru ar putea deruta programatorul obișnuit: este mai bine să-l numiți așa cum ar fi userActivityGroup sau userActivities.

Căutare

Unul dintre dezavantajele numelor scurte și simple este că ele sunt greu de găsit într-un corp mare de cod — Care ar fi mai ușor de găsit: „nume” sau „NAME_FOR_DEFAULT_USER”? A doua varianta, desigur. Ar trebui să evităm cuvintele (litere) întâlnite frecvent în nume, deoarece acestea vor crește doar numărul de fișiere care se potrivesc în timpul unei căutări, ceea ce nu este bine. Aș dori să vă reamintesc că programatorii petrec mai mult timp citind codul decât scriendu-l, așa că fiți inteligent în a numi elementele aplicației dvs. Dar dacă un nume bun pur și simplu nu poate fi găsit? Ce se întâmplă dacă numele unei metode nu descrie bine funcționalitatea acesteia? Aici intră în scenă comentariile.

Comentarii

Reguli de codificare: puterea numelor corecte, a comentariilor bune și rele - 4Nu există nimic mai bun decât un comentariu pertinent, dar nimic nu aglomera un modul precum comentariile vacue, învechite sau false. Pot fi o sabie cu două tăișuri, nu? Totuși, nu ar trebui să tratezi comentariile ca fiind fără ambiguitate bune, ci mai degrabă ca pe un rău mai mic. La urma urmei, un comentariu este în esență o modalitate de a compensa gândirea care nu apare clar în cod. De exemplu, le folosim pentru a transmite cumva esența unei metode, dacă metoda în sine se dovedește a fi prea confuză. În această situație, este mai bine să refactorizați corect codul decât să scrieți note descriptive. Cu cât comentariul este mai vechi, cu atât comentariul este mai rău, deoarece codul tinde să crească și să evolueze, dar comentariile pot rămâne aceleași. Cu cât a trecut mai mult timp de la crearea unui comentariu, cu atât acesta poate fi mai discutabil. Comentariile inexacte sunt mult mai rele decât fără comentarii, deoarece sunt confuze și înșelătoare, oferind așteptări false. Și chiar dacă avem un cod foarte complicat, ar trebui să-l rescriem mai degrabă decât să-l comentăm.

Tipuri de comentarii

  • Comentarii juridice — Comentarii la începutul fiecărui fișier sursă din motive legale, de exemplu:

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

  • Comentarii informative — Comentarii care reprezintă o explicație a codului (furnizează informații suplimentare sau explică intenția unei anumite secțiuni de cod).

    De exemplu:

    
    /*
    * 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()
           );
           }
    

    În acest caz, puteți face fără comentarii, deoarece numele metodei și parametrii acesteia, împreună cu funcționalitatea foarte transparentă, se descriu bine.

  • Comentarii de avertizare — Comentariu destinat să avertizeze alți dezvoltatori despre consecințele nedorite ale unei acțiuni (de exemplu, avertizându-i despre motivul pentru care un test a fost marcat ca @Ignorare):

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

  • TODO — Comentarii care sunt o notă despre ceva ce trebuie făcut în viitor, dar din anumite motive nu poate fi făcut acum. Aceasta este o practică bună, dar astfel de comentarii ar trebui revizuite în mod regulat pentru a le elimina pe cele irelevante și pentru a evita dezordinea.

    Un exemplu ar fi:

    
    // 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);
           }
    

    Aici remarcăm faptul că trebuie să adăugăm o comparație a utilizatorului care efectuează operația de descărcare (al cărui ID îl vom extrage din contextul de securitate) cu cel care a efectuat operația de salvare.

  • Comentarii de întărire — Comentarii care subliniază importanța unei circumstanțe care la prima vedere poate părea nesemnificativă.

    Ca exemplu, luați în considerare o parte dintr-o metodă care umple o bază de date de testare cu câteva scripturi:

    
    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
    

  • Comentarii Javadoc — Comentarii care descriu API-ul pentru anumite funcționalități. Există probabil cele mai utile comentarii, deoarece API-ul documentat este mult mai ușor de lucrat. Acestea fiind spuse, pot fi, de asemenea, învechite ca orice alt tip de comentariu. Așadar, nu uitați niciodată că principala contribuție la documentare este adusă nu de comentarii, ci de cod bun.

    Iată un exemplu de metodă destul de comună pentru actualizarea unui utilizator:

    
    /**
    * 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);
    

Comentarii proaste

  • comentariu mormăiitor — Comentarii care de obicei sunt scrise în grabă și a căror semnificație este de înțeles doar dezvoltatorului care le-a scris, deoarece doar el sau ea percepe situația nuanțată la care se referă comentariul.

    Luați în considerare acest exemplu:

    
    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 
          }
    }
    

    Cine încarcă aceste setări? Au fost deja încărcate? Această metodă ar trebui să prindă excepții și să încarce setările implicite? Apar prea multe întrebări la care se poate răspunde doar prin aprofundarea unei investigații a altor părți ale sistemului.

  • Comentarii redundante — Comentarii care nu au nicio încărcare semantică, deoarece ceea ce se întâmplă într-o anumită secțiune a codului este foarte clar. Cu alte cuvinte, comentariul nu este mai ușor de citit decât codul.

    Să vedem un exemplu:

    
    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;
       }
    

    Ce rost au astfel de comentarii? Tot ce explică ei este deja perfect clar.

  • Comentarii nesigure — Comentarii neadevărate și doar înșelătoare (dezinformare). De exemplu, iată unul.

    
    /**
    * 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();
    }
    

    Ce este în neregulă cu acest comentariu? Faptul că ne minte puțin, în sensul că conexiunea este închisă dacă isNotUsing este fals, nu invers, după cum ne informează comentariul.

  • Comentarii obligatorii — Comentarii care sunt considerate obligatorii (de exemplu, comentarii Javadoc), dar care, de fapt, uneori se adună excesiv și sunt nesigure și inutile (trebuie să vă gândiți dacă aceste comentarii sunt cu adevărat necesare).

  • Exemplu:

    
    /**
    * 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);
    

    Ați putea înțelege ce face metoda fără aceste comentarii? Cel mai probabil, da, deci comentariile devin inutile aici.

  • Comentarii în jurnal — Comentarii care sunt uneori adăugate la începutul unui modul de fiecare dată când este editat (ceva ca un jurnal de modificări).

    
    /**
    * 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;
    */
    

    Această abordare a fost odată justificată, dar odată cu apariția sistemelor de control al versiunilor (de exemplu, Git), a devenit o dezordine și o complicație inutilă a codului.

  • Comentarii de autor — Comentarii al căror scop este să indice persoana care a scris codul, astfel încât să o puteți contacta și să discutați cum, ce și de ce, de exemplu:

    
    * @author Bender Bending
    

    Încă o dată, sistemele de control al versiunilor își amintesc exact cine a adăugat orice fragment de cod și când, așa că această abordare este de prisos.

  • Cod comentat — Cod care a fost comentat dintr-un motiv sau altul. Acesta este unul dintre cele mai rele obiceiuri, pentru că ceea ce se întâmplă este că comentezi ceva și îl uiți, iar apoi alți dezvoltatori pur și simplu nu au curajul să-l ștergă (la urma urmei, ce se întâmplă dacă este ceva valoros?).

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

    Drept urmare, codul comentat se acumulează ca un gunoi. În niciun caz nu trebuie să lăsați un astfel de cod. Dacă chiar ai nevoie de el, nu uita de sistemul de control al versiunilor.

  • Comentarii neevidente — Comentarii care descriu ceva într-un mod excesiv de complicat.

    
    /*
        * 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];
    

    Un comentariu ar trebui să explice codul. Nu ar trebui să aibă nevoie de o explicație. Deci, ce este în neregulă aici? Ce sunt „octeții de filtrare”? Despre ce este acel „+ 1”? De ce exact 300?

Dacă te-ai hotărât deja să scrii comentarii, iată câteva sfaturi:
  1. Folosiți stiluri care sunt ușor de întreținut: menținerea stilurilor care sunt prea fanteziste și exotice este enervantă și necesită timp.
  2. Nu utilizați comentarii de sfârșit de rând care se referă la rânduri simple: rezultatul este o grămadă mare de comentarii. În plus, este dificil să te gândești la un comentariu semnificativ pentru fiecare rând.
  3. Când scrieți un comentariu, încercați să răspundeți la întrebarea „de ce”, nu „cum”.
  4. Evitați informațiile prescurtate. După cum am spus mai sus, nu avem nevoie de o explicație pentru un comentariu: comentariul în sine este explicația.
  5. Puteți utiliza comentariile pentru a nota unitățile și intervalele de valori.
  6. Plasați comentariile aproape de codul pe care îl descriu.
În cele din urmă, încă vreau să vă reamintesc că cel mai bun comentariu este nici un comentariu, ci mai degrabă utilizarea unei denumiri abile în aplicația dvs. De regulă, de cele mai multe ori vom lucra cu codul existent, menținându-l și extinzându-l. Este mult mai convenabil atunci când acest cod este ușor de citit și de înțeles, deoarece codul prost este un obstacol. Este ca și cum ai arunca o cheie în lucru, iar graba este tovarășul său fidel. Și cu cât avem mai mult cod prost, cu atât performanța scade. Aceasta înseamnă că trebuie să refactorăm din când în când. Dar dacă de la început încerci să scrii cod care nu va determina următorii dezvoltatori să vrea să te găsească și să te omoare, atunci nu va trebui să-l refactorizezi la fel de des. Dar va fi în continuare necesar, deoarece condițiile și cerințele produsului se modifică în mod constant odată cu adăugarea de noi dependențe și conexiuni. Ei bine, cred că asta e tot pentru mine azi. Multumesc tuturor celor care au citit pana aici :)