CodeGym /Java blogg /Slumpmässig /Kodningsregler: kraften i korrekta namn, bra och dåliga k...
John Squirrels
Nivå
San Francisco

Kodningsregler: kraften i korrekta namn, bra och dåliga kommentarer

Publicerad i gruppen
Kodningsregler: kraften i korrekta namn, bra och dåliga kommentarer - 1Hur ofta har du behövt gräva i någon annans kod? Istället för två timmar kan du spendera två dagar på att helt enkelt förstå logiken i vad som händer. Det roliga är att för den som skrev koden är allt tydligt och helt transparent. Detta är inte förvånande: trots allt är perfekt kod ett mycket vagt koncept, eftersom varje utvecklare har sin egen vision av världen och koden också. Mer än en gång har jag varit i en situation när en kollega och jag tittade på samma kod och hade olika åsikter om dess riktighet och renhet.Kodningsregler: kraften i korrekta namn, bra och dåliga kommentarer - 2Låter det bekant, eller hur? Ändå finns det några beprövade principer som bör följas. I slutändan kommer de att vara fördelaktiga för dig, för om du lämnar din kod i det tillstånd som du själv skulle vilja ta emot den i, så skulle världen bli lite gladare och renare. I vår tidigare artikel(eller snarare en liten guide) om kodningsregler, vi fick lite känsla för rekommendationer för att skriva ett system som helhet och dess beståndsdelar, såsom objekt, gränssnitt, klasser, metoder och variabler. I samma artikel nämnde jag slentrianmässigt det korrekta namnet på vissa element. Jag skulle vilja prata om detta idag, eftersom korrekta namn gör koden många gånger lättare att läsa. Vi kommer att avsluta ämnet korrekt kod med några reflektioner, små exempel på kommentarer i kod, och en övervägande om detta är bra eller inte så bra. Nåväl, låt oss börja.

Rätt namn

Korrekta namn förbättrar kodens läsbarhet, vilket minskar tiden som krävs för att bekanta dig med koden, eftersom det är mycket lättare att använda en metod när dess namn i grova drag beskriver dess funktionalitet. Allt i kod består av namn (variabler, metoder, klasser, objekt, filer etc.), så denna punkt blir väldigt viktig när man skapar korrekt, ren kod. Baserat på ovanstående bör namnet förmedla betydelse, till exempel varför variabeln finns, vad den gör och hur den används. Jag kommer att notera mer än en gång att den bästa kommentaren för en variabel är att ge den ett bra namn.Kodningsregler: kraften i korrekta namn, bra och dåliga kommentarer - 3

från TV-serien "Sherlock" (2010-2017)

Namngivningsgränssnitt

Gränssnitt har vanligtvis namn som börjar med stor bokstav och skrivs i CamelCase. När man skrev ett gränssnitt ansågs det tidigare vara bra att lägga till prefixet "I" för att beteckna det som ett gränssnitt (till exempel IUserService), men det ser ganska fult och distraherande ut. I sådana fall är det bättre att utelämna prefixet (UserService) och lägga till "Impl" som ett suffix till namnet på dess implementering (t.ex. UserServiceImpl). Eller eventuellt, som en sista utväg, lägg till ett "C"-prefix till namnet på implementeringen (t.ex. CUserService).

Klassnamn

Precis som gränssnitt, är klassnamn versaler och använder CamelCase. Det spelar ingen roll om vi står inför en zombieapokalyps, det spelar ingen roll om slutet är nära – aldrig, aldrig, aldrig ska namnet på en klass vara ett verb! Klass- och objektnamn måste vara substantiv eller sammansatta substantiv (UserController, UserDetails, UserAccount, och så vidare). Du bör inte fästa förkortningen av applikationen på slutet av namnet på varje klass, eftersom det bara skulle lägga till onödig komplexitet. Till exempel, om vi har en User Data Migration-applikation, lägg inte till "UDM" till varje klass, dvs UDMUserDetails, UDMUserAccount, UDMUserController.

Metodnamn

Vanligtvis börjar metodnamn med en liten bokstav, men de använder också kamelstil (camelCase). Ovan sa vi att klassnamn aldrig får vara verb. Här är situationen precis den motsatta: namnen på metoderna ska vara verb eller verbfraser: findUserById, findAllUsers, createUser, och så vidare. När du skapar en metod (liksom variabler och klasser), använd därför en konsekvent namnkonvention för att undvika förvirring. För att till exempel hitta en användare kan en metod heta getUserById eller findUserById. Och en sak till: använd inte humor i namnen på metoderna, för andra kanske inte förstår skämtet. Som ett resultat kan de misslyckas med att förstå vad metoden gör.

Variabelnamn

I de flesta fall börjar variabelnamn med en liten bokstav och använder även camelCase, förutom när variabeln är en global konstant. I sådana fall skrivs alla bokstäver i namnet med versaler och orden separeras med ett understreck ("_"). För enkelhetens skull kan du använda meningsfull kontext när du namnger variabler. Med andra ord, när en variabel existerar som en del av något större, till exempel förnamn, efternamn eller status. I sådana fall kan du lägga till ett prefix som indikerar objektet som denna variabel tillhör. Till exempel: userFirstName, userLastName, userStatus. Du bör också undvika liknande namn för variabler när de har helt olika betydelser. Här är några vanliga antonymer som används i variabelnamn:
  • börja/sluta
  • första/sista
  • låst/upplåst
  • min Max
  • nästa/föregående
  • gammal ny
  • öppnat/stängt
  • synlig/osynlig
  • källa/mål
  • källdestination
  • upp ner

Korta variabelnamn

När vi har variabler som x eller n eller något liknande ser vi inte direkt avsikten med personen som skrev koden. Det är inte självklart vad n gör. Att räkna ut det kräver mer noggrann kontemplation (och detta betyder tid, tid, tid). Anta till exempel att vi har ett fält som representerar den ansvariga användarens id. Istället för något variabelnamn som x eller helt enkelt id kommer vi att döpa denna variabel till "responsibleUserId", vilket omedelbart förbättrar läsbarheten och informationsinnehållet. Som sagt, korta namn som n har en plats som lokala variabler i små metoder, där kodblocket som involverar denna variabel bara är ett par rader långt, och metodnamnet beskriver perfekt vad som händer där. När en utvecklare ser en sådan variabel förstår den att den är av underordnad betydelse och har en mycket begränsad omfattning. Som ett resultat har omfattningen ett visst beroende av ett variabelnamns längd: ju längre namnet är, desto mer globalt är variabeln och vice versa. Som ett exempel, här är en metod för att hitta den senast sparade användaren efter datum:

public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
Här använder vi kortnamnade variabler x och y för att sortera strömmen, och sedan glömmer vi bort dem.

Optimal längd

Låt oss fortsätta med ämnet namnlängd. Den optimala namnlängden är någonstans mellan n och maximumNumberOfUsersInTheCurrentGroup. Korta namn lider med andra ord av bristande betydelse, medan namn som är för långa förlänger programmet utan att öka läsbarheten, och vi är helt enkelt för lata för att skriva dem varje gång. Förutom fallet som beskrivs ovan för variabler med ett kort namn som n, bör du hålla dig till en längd på cirka 8-16 tecken. Detta är inte en strikt regel, bara en riktlinje.

Små skillnader

Jag kan inte låta bli att nämna subtila skillnader i namn. Detta är också en dålig praxis, eftersom dessa skillnader helt enkelt kan vara förvirrande eller kräva att man lägger mycket extra tid på att lägga märke till dem. Till exempel är skillnaden mellan InvalidDataAccessApiUsageException och InvalidDataAccessResourceUsageException svår att upptäcka med ett ögonkast. Förvirring kan också ofta uppstå när man använder gemener L och O, eftersom de lätt kan misstas för 1 och 0. I vissa typsnitt är skillnaden mer påtaglig, i vissa är den mindre.

Meningen

Vi behöver göra namn meningsfulla, men inte skapa oklarheter genom synonymer, eftersom till exempel UserData och UserInfo faktiskt har samma betydelse. I det här fallet skulle vi behöva gräva djupare i koden för att förstå vilket särskilt objekt vi behöver. Undvik ord som inte förmedlar användbar information. Till exempel, i firstNameString, varför behöver vi ordet String? Kan detta verkligen vara ett Date-objekt? Självklart inte. Så vi använder helt enkelt firstName. Jag skulle också vilja nämna booleska variabler. Som ett exempel, ta en boolean som heter flagDeleted. Ordet flagga har ingen betydelse. Det är rimligare att kalla det för raderad.

Desinformation

Jag skulle också vilja säga några ord om felaktiga namnkonventioner. Låt oss säga att vi har en variabel som heter userActivityList, men istället för att vara en List är det här objektet någon annan containertyp eller anpassat lagringsobjekt. Detta kan förvirra den genomsnittliga programmeraren: det är bättre att kalla det något som userActivityGroup eller userActivities.

Sök

En av nackdelarna med korta och enkla namn är att de är svåra att hitta i en stor mängd kod — vilket skulle vara lättare att hitta: "name" eller "NAME_FOR_DEFAULT_USER"? Det andra alternativet, förstås. Vi bör undvika ofta förekommande ord (bokstäver) i namn, eftersom de bara kommer att öka antalet matchande filer under en sökning, vilket inte är bra. Jag skulle vilja påminna dig om att programmerare lägger mer tid på att läsa kod än att skriva den, så var smart med att namnge elementen i din applikation. Men vad händer om ett bra namn bara inte kan hittas? Vad händer om namnet på en metod inte beskriver dess funktionalitet väl? Det är här kommentarerna kommer in på scenen.

Kommentarer

Kodningsregler: kraften i korrekta namn, bra och dåliga kommentarer - 4Det finns inget bättre än en relevant kommentar, men ingenting stör en modul som tomma, inaktuella eller falska kommentarer. De kan vara ett tveeggat svärd, eller hur? Ändå ska du inte behandla kommentarer som entydigt bra, utan snarare som ett mindre ont. När allt kommer omkring är en kommentar i grunden ett sätt att kompensera för tänkande som inte kommer fram tydligt i koden. Till exempel använder vi dem för att på något sätt förmedla essensen av en metod, om själva metoden visar sig vara för förvirrande. I den här situationen är det bättre att omfaktorera koden korrekt än att skriva beskrivande anteckningar. Ju äldre kommentaren är, desto sämre kommentar, eftersom koden tenderar att växa och utvecklas, men kommentarerna kan förbli desamma. Ju längre tid som har gått sedan en kommentar skapades, desto mer tveksam kan den vara. Felaktiga kommentarer är mycket värre än inga kommentarer alls, eftersom de är förvirrande och vilseledande och ger falska förväntningar. Och även om vi har väldigt knepig kod bör vi skriva om den istället för att kommentera den.

Typer av kommentarer

  • Juridiska kommentarer — Kommentarer i början av varje källfil av juridiska skäl, till exempel:

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

  • Informativa kommentarer — Kommentarer som representerar en förklaring av koden (tillhandahåller ytterligare information eller förklarar avsikten med ett givet avsnitt av koden).

    Till exempel:

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

    I det här fallet kan du klara dig utan kommentarer, eftersom namnet på metoden och dess parametrar, tillsammans med mycket transparent funktionalitet, beskriver sig själva väl.

  • Varningskommentarer — Kommentar avsedd att varna andra utvecklare om de oönskade konsekvenserna av en åtgärd (till exempel varna dem om varför ett test markerades som @Ignorera):

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

  • TODO — Kommentarer som är en anteckning om något som behöver göras i framtiden som men av någon anledning inte kan göras nu. Detta är en bra praxis, men sådana kommentarer bör granskas regelbundet för att ta bort irrelevanta och undvika röran.

    Ett exempel skulle vara:

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

    Här noterar vi det faktum att vi behöver lägga till en jämförelse av användaren som utför nedladdningsoperationen (vars ID vi kommer att extrahera från säkerhetskontexten) med den som utförde sparaoperationen.

  • Förstärkande kommentarer — Kommentarer som betonar vikten av en omständighet som vid första anblicken kan verka obetydlig.

    Som ett exempel, överväg en del av en metod som fyller en testdatabas med några skript:

    
    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-kommentarer — Kommentarer som beskriver API:et för viss funktionalitet. Det finns förmodligen de mest användbara kommentarerna, eftersom det dokumenterade API:et är mycket lättare att arbeta med. Som sagt, de kan också vara föråldrade som alla andra typer av kommentarer. Så glöm aldrig att det huvudsakliga bidraget till dokumentationen inte görs av kommentarer, utan av bra kod.

    Här är ett exempel på en ganska vanlig metod för att uppdatera en användare:

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

Dåliga kommentarer

  • muttrande kommentar — Kommentarer som vanligtvis skrivs i en hast och vars innebörd bara är förståelig för utvecklaren som skrev dem, eftersom bara han eller hon uppfattar den nyanserade situationen som kommentaren hänvisar till.

    Tänk på det här exemplet:

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

    Vem laddar dessa inställningar? Har de redan laddats? Är den här metoden tänkt att fånga undantag och ladda standardinställningar? Alltför många frågor uppstår som bara kan besvaras genom att fördjupa sig i en undersökning av andra delar av systemet.

  • Redundanta kommentarer — Kommentarer som inte bär någon semantisk belastning, eftersom vad som händer i ett givet avsnitt av koden är mycket tydligt. Kommentaren är med andra ord inte lättare att läsa än koden.

    Låt oss se ett exempel:

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

    Vad är poängen med sådana kommentarer? Allt de förklarar är redan helt klart.

  • Otillförlitliga kommentarer — Kommentarer som är osanna och bara vilseledande (desinformation). Till exempel, här är en.

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

    Vad är det för fel på den här kommentaren? Det faktum att det ljuger för oss lite, i och med att kopplingen stängs om isNotUsing är falskt, inte tvärtom, som kommentaren informerar oss om.

  • Obligatoriska kommentarer — Kommentarer som anses obligatoriska (t.ex. Javadoc-kommentarer), men som faktiskt ibland hopar sig överdrivet och är opålitliga och onödiga (du måste tänka på om dessa kommentarer verkligen behövs).

  • Exempel:

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

    Skulle du kunna förstå vad metoden gör utan dessa kommentarer? Troligtvis, ja, så kommentarer blir meningslösa här.

  • Loggkommentarer — Kommentarer som ibland läggs till i början av en modul varje gång den redigeras (något som en ändringslogg).

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

    Detta tillvägagångssätt var en gång motiverat, men med tillkomsten av versionskontrollsystem (till exempel Git) blev det en onödig röra och komplikation av koden.

  • Författarskapskommentarer — Kommentarer vars syfte är att ange personen som skrev koden, så att du kan kontakta honom/henne och diskutera hur, vad och varför, t.ex.

    
    * @author Bender Bending
    

    Än en gång minns versionskontrollsystemen exakt vem som lade till någon kodbit och när, så detta tillvägagångssätt är överflödigt.

  • Utkommen kod — Kod som kommenterades ut av en eller annan anledning. Det här är en av de värsta vanorna, för det som händer är att du kommenterar något och glömmer det, och då har andra utvecklare helt enkelt inte modet att ta bort det (tänk om det är något värdefullt?).

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

    Som ett resultat ackumuleras kommenterad kod som skräp. I inget fall bör du lämna en sådan kod. Om du verkligen behöver det, glöm inte versionskontrollsystemet.

  • Icke-uppenbara kommentarer — Kommentarer som beskriver något på ett alltför komplicerat sätt.

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

    En kommentar bör förklara koden. Det borde i sig inte behöva en förklaring. Så vad är det för fel här? Vad är "filterbytes"? Vad handlar det där "+1" om? Varför just 300?

Om du redan har bestämt dig för att skriva kommentarer, här är ett par tips:
  1. Använd stilar som är lätta att underhålla: att behålla stilar som är för snygga och exotiska är irriterande och tidskrävande.
  2. Använd inte slutkommentarer som hänvisar till enstaka rader: resultatet är en stor hög med kommentarer. Dessutom är det svårt att komma på en meningsfull kommentar för varje rad.
  3. När du skriver en kommentar, försök att svara på frågan "varför", inte "hur".
  4. Undvik förkortad information. Som jag sa ovan, vi behöver ingen förklaring för en kommentar: själva kommentaren är förklaringen.
  5. Du kan använda kommentarer för att anteckna enheter och värdeintervall.
  6. Lägg kommentarer nära koden de beskriver.
Slutligen vill jag ändå påminna dig om att den bästa kommentaren är ingen kommentar, utan snarare användningen av skickliga namngivningar genom hela din ansökan. Som regel kommer vi för det mesta att arbeta med befintlig kod, underhålla och utöka den. Det är mycket bekvämare när den här koden är lätt att läsa och förståelig, eftersom dålig kod är ett hinder. Det är som att kasta en skiftnyckel i arbetet, och brådskan är dess trogna följeslagare. Och ju mer dålig kod vi har, desto mer sjunker prestandan. Det betyder att vi behöver refaktorera då och då. Men om du från början försöker skriva kod som inte kommer att få nästa utvecklare att vilja hitta och döda dig, då behöver du inte refaktorera den så ofta. Men det kommer fortfarande att vara nödvändigt, eftersom produktens förutsättningar och krav ständigt förändras med nya beroenden och kopplingar. Tja, jag antar att det var allt för mig idag. Tack alla som läst så här långt :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION