CodeGym /Java Blog /Willekeurig /Codeerregels: de kracht van juiste namen, goede en slecht...
John Squirrels
Niveau 41
San Francisco

Codeerregels: de kracht van juiste namen, goede en slechte opmerkingen

Gepubliceerd in de groep Willekeurig
Codeerregels: de kracht van juiste namen, goede en slechte opmerkingen - 1Hoe vaak heb je in de code van iemand anders moeten graven? In plaats van twee uur kun je twee dagen besteden aan het simpelweg begrijpen van de logica van wat er gebeurt. Het grappige is dat voor degene die de code heeft geschreven, alles duidelijk en volledig transparant is. Dat is niet verwonderlijk: perfecte code is immers een heel vaag begrip, want elke ontwikkelaar heeft ook zijn eigen visie op de wereld en op de code. Meer dan eens ben ik in een situatie geweest waarin een collega en ik naar dezelfde code keken en verschillende meningen hadden over de juistheid en netheid ervan.Codeerregels: de kracht van correcte namen, goede en slechte commentaren - 2Klinkt bekend, nietwaar? Toch zijn er enkele beproefde principes waaraan moet worden vastgehouden. Uiteindelijk zijn ze voordelig voor u, want als u uw code achterlaat in de staat waarin u deze zelf zou willen ontvangen, wordt de wereld een beetje gelukkiger en schoner. In ons vorige artikel(of liever, kleine handleiding) over coderingsregels, kregen we een beetje idee van aanbevelingen voor het schrijven van een systeem als geheel en zijn samenstellende delen, zoals objecten, interfaces, klassen, methoden en variabelen. In datzelfde artikel noemde ik terloops de juiste naamgeving van bepaalde elementen. Ik wil het hier vandaag graag over hebben, omdat correcte namen de code vele malen leesbaarder maken. We sluiten het onderwerp correcte code af met enkele reflecties, kleine voorbeelden van opmerkingen in code en een overweging of dit goed of niet zo goed is. Nou, laten we beginnen.

Juiste namen

Correcte namen verbeteren de leesbaarheid van de code, waardoor de tijd die nodig is om vertrouwd te raken met de code wordt verkort, omdat het gebruik van een methode veel gemakkelijker is wanneer de naam de functionaliteit ervan ruwweg beschrijft. Alles in code bestaat uit namen (variabelen, methoden, klassen, objecten, bestanden, enz.), dus dit punt wordt erg belangrijk bij het maken van correcte, schone code. Op basis van het bovenstaande moet de naam betekenis overbrengen, bijvoorbeeld waarom de variabele bestaat, wat deze doet en hoe deze wordt gebruikt. Ik zal meer dan eens opmerken dat de beste opmerking voor een variabele is om hem een ​​goede naam te geven.Codeerregels: de kracht van correcte namen, goede en slechte commentaren - 3

uit tv-serie "Sherlock" (2010-2017)

Interfaces benoemen

Interfaces hebben meestal namen die beginnen met een hoofdletter en zijn geschreven in CamelCase. Bij het schrijven van een interface werd het vroeger als een goede gewoonte beschouwd om het voorvoegsel "I" toe te voegen om het aan te duiden als een interface (bijvoorbeeld IUserService), maar dat ziet er behoorlijk lelijk en afleidend uit. In dergelijke gevallen is het beter om het voorvoegsel (UserService) weg te laten en "Impl" als achtervoegsel toe te voegen aan de naam van de implementatie (bijv. UserServiceImpl). Of eventueel, als laatste redmiddel, een "C" prefix toevoegen aan de naam van de implementatie (bijv. CUserService).

Klasse namen

Net als bij interfaces worden klassenamen met een hoofdletter geschreven en wordt CamelCase gebruikt. Het maakt niet uit of we voor een zombie-apocalyps staan, het maakt niet uit of het einde nabij is - nooit, nooit, nooit mag de naam van een klasse een werkwoord zijn! Klasse- en objectnamen moeten zelfstandige naamwoorden of samengestelde zelfstandige naamwoorden zijn (UserController, UserDetails, UserAccount, enzovoort). U moet de afkorting van de toepassing niet aan het einde van de naam van elke klasse plakken, omdat dat alleen maar voor onnodige complexiteit zou zorgen. Als we bijvoorbeeld een toepassing voor migratie van gebruikersgegevens hebben, voeg dan niet "UDM" toe aan elke klasse, dwz UDMUserDetails, UDMUserAccount, UDMUserController.

Methode namen

Gewoonlijk beginnen methodenamen met een kleine letter, maar ze gebruiken ook camel case-stijl (camelCase). Hierboven zeiden we dat klassennamen nooit werkwoorden mogen zijn. Hier is de situatie precies het tegenovergestelde: de namen van de methoden moeten werkwoorden of werkwoorduitdrukkingen zijn: findUserById, findAllUsers, createUser, enzovoort. Gebruik bij het maken van een methode (evenals variabelen en klassen) dus een consistente naamgevingsconventie om verwarring te voorkomen. Om bijvoorbeeld een gebruiker te vinden, kan een methode getUserById of findUserById heten. En nog een ding: gebruik geen humor in de namen van de methoden, omdat anderen de grap misschien niet begrijpen. Als gevolg hiervan kunnen ze niet begrijpen wat de methode doet.

Variabele namen

In de meeste gevallen beginnen namen van variabelen met een kleine letter en gebruiken ze ook camelCase, behalve wanneer de variabele een globale constante is. In dergelijke gevallen worden alle letters van de naam in hoofdletters geschreven en worden de woorden gescheiden door een liggend streepje ("_"). Voor het gemak kunt u betekenisvolle context gebruiken bij het benoemen van variabelen. Met andere woorden, wanneer een variabele bestaat als onderdeel van iets groters, bijvoorbeeld voornaam, achternaam of status. In dergelijke gevallen kunt u een voorvoegsel toevoegen dat aangeeft tot welk object deze variabele behoort. Bijvoorbeeld: gebruikerVoornaam, gebruikerAchternaam, gebruikerStatus. Vermijd ook gelijkaardige namen voor variabelen als ze totaal verschillende betekenissen hebben. Hier volgen enkele vaak voorkomende antoniemen die in variabelenamen worden gebruikt:
  • beginnen/eindigen
  • eerste Laatste
  • vergrendeld/ontgrendeld
  • min/max
  • volgende vorige
  • oud nieuw
  • geopend/gesloten
  • zichtbaar/onzichtbaar
  • bron/doel
  • bron bestemming
  • op neer

Korte namen van variabelen

Als we variabelen als x of n of iets dergelijks hebben, zien we niet meteen de bedoeling van de persoon die de code heeft geschreven. Het is niet duidelijk wat n doet. Om dat uit te zoeken, is meer zorgvuldige contemplatie vereist (en dit betekent tijd, tijd, tijd). Stel dat we een veld hebben dat de id van de verantwoordelijke gebruiker vertegenwoordigt. In plaats van een variabelenaam zoals x of simpelweg id, noemen we deze variabele "responsibleUserId", wat de leesbaarheid en informatie-inhoud onmiddellijk verbetert. Dat gezegd hebbende, korte namen zoals n hebben een plaats als lokale variabelen in kleine methoden, waar het codeblok met betrekking tot deze variabele slechts een paar regels lang is, en de naam van de methode beschrijft perfect wat daar gebeurt. Bij het zien van een dergelijke variabele begrijpt een ontwikkelaar dat deze van secundair belang is en een zeer beperkte reikwijdte heeft. Hierdoor is het bereik in zekere mate afhankelijk van de lengte van een variabelenaam: hoe langer de naam, hoe globaler de variabele en vice versa. Hier is bijvoorbeeld een methode om de laatst opgeslagen gebruiker op datum te vinden:

public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
Hier gebruiken we korte variabelen x en y om de stream te sorteren, en daarna vergeten we ze.

Optimale lengte

Laten we doorgaan met het onderwerp naamlengte. De optimale naamlengte ligt ergens tussen n en maximumNumberOfUsersInTheCurrentGroup. Met andere woorden, korte namen lijden aan een gebrek aan betekenis, terwijl namen die te lang zijn het programma verlengen zonder de leesbaarheid toe te voegen, en we zijn simpelweg te lui om ze elke keer op te schrijven. Afgezien van het hierboven beschreven geval voor variabelen met een korte naam zoals n, moet u zich houden aan een lengte van ongeveer 8-16 tekens. Dit is geen strikte regel, maar een richtlijn.

Kleine verschillen

Ik kan niet nalaten subtiele verschillen in namen te noemen. Dit is ook een slechte gewoonte, omdat deze verschillen eenvoudigweg verwarrend kunnen zijn of veel extra tijd kunnen kosten om ze op te merken. Het verschil tussen InvalidDataAccessApiUsageException en InvalidDataAccessResourceUsageException is bijvoorbeeld moeilijk in één oogopslag te zien. Er kan ook vaak verwarring ontstaan ​​bij het gebruik van kleine letters L en O, omdat ze gemakkelijk kunnen worden aangezien voor 1 en 0. In sommige lettertypen is het verschil duidelijker, in andere minder.

De betekenis

We moeten namen zinvol maken, maar geen dubbelzinnigheid creëren door middel van synoniemen, aangezien bijvoorbeeld UserData en UserInfo eigenlijk dezelfde betekenis hebben. In dit geval zouden we dieper in de code moeten graven om te begrijpen welk specifiek object we nodig hebben. Vermijd woorden die geen nuttige informatie overbrengen. Waarom hebben we bijvoorbeeld in firstNameString het woord String nodig? Zou dit echt een Date-object kunnen zijn? Natuurlijk niet. We gebruiken dus gewoon voornaam. Ik zou ook booleaanse variabelen willen noemen. Neem als voorbeeld een boolean met de naam flagDeleted. Het woord vlag heeft geen betekenis. Het is redelijker om het isDeleted te noemen.

desinformatie

Ik wil ook een paar woorden zeggen over onjuiste naamgevingsconventies. Laten we zeggen dat we een variabele hebben met de naam userActivityList, maar in plaats van een lijst te zijn, is dit object een ander containertype of een aangepast opslagobject. Dit zou de gemiddelde programmeur in verwarring kunnen brengen: het is beter om het zoiets als userActivityGroup of userActivities te noemen.

Zoekopdracht

Een van de nadelen van korte en eenvoudige namen is dat ze moeilijk te vinden zijn in een grote hoeveelheid code. Wat is gemakkelijker te vinden: "name" of "NAME_FOR_DEFAULT_USER"? De tweede optie natuurlijk. We moeten vaak voorkomende woorden (letters) in namen vermijden, omdat ze het aantal overeenkomende bestanden alleen maar verhogen tijdens een zoekopdracht, wat niet goed is. Ik wil u eraan herinneren dat programmeurs meer tijd besteden aan het lezen van code dan aan het schrijven ervan, dus wees slim met het benoemen van de elementen van uw toepassing. Maar wat als een goede naam gewoon niet kan worden gevonden? Wat als de naam van een methode de functionaliteit niet goed beschrijft? Dit is waar opmerkingen het podium betreden.

Opmerkingen

Codeerregels: de kracht van correcte namen, goede en slechte commentaren - 4Er is niets beters dan een relevante opmerking, maar niets maakt een module zo rommelig als inhoudsloze, verouderde of valse opmerkingen. Ze kunnen een tweesnijdend zwaard zijn, toch? Toch moet je opmerkingen niet als ondubbelzinnig goed beschouwen, maar eerder als een minder kwaad. Een opmerking is immers in wezen een manier om gedachten te compenseren die niet duidelijk in de code tot uiting komen. We gebruiken ze bijvoorbeeld om op de een of andere manier de essentie van een methode over te brengen, als de methode zelf te verwarrend blijkt te zijn. In deze situatie is het beter om de code correct te herstructureren dan om beschrijvende notities te schrijven. Hoe ouder de opmerking, hoe slechter de opmerking, omdat code de neiging heeft om te groeien en te evolueren, maar opmerkingen kunnen hetzelfde blijven. Hoe meer tijd er is verstreken sinds een opmerking is gemaakt, hoe twijfelachtiger deze kan zijn. Onnauwkeurige opmerkingen zijn veel erger dan helemaal geen opmerkingen, omdat ze verwarrend en bedrieglijk zijn en valse verwachtingen wekken. En zelfs als we zeer lastige code hebben, moeten we deze herschrijven in plaats van er commentaar op te geven.

Soorten opmerkingen

  • Juridische opmerkingen — Opmerkingen aan het begin van elk bronbestand om juridische redenen, bijvoorbeeld:

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

  • Informatieve opmerkingen — Opmerkingen die een uitleg van de code vertegenwoordigen (met aanvullende informatie of uitleg over de bedoeling van een bepaald gedeelte van de code).

    Bijvoorbeeld:

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

    In dit geval kunt u het zonder commentaar stellen, aangezien de naam van de methode en zijn parameters, gekoppeld aan een zeer transparante functionaliteit, zichzelf goed omschrijven.

  • Waarschuwingsopmerkingen - Opmerking bedoeld om andere ontwikkelaars te waarschuwen voor de ongewenste gevolgen van een actie (bijvoorbeeld om hen te waarschuwen waarom een ​​test is gemarkeerd als @Ignore):

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

  • TODO - Opmerkingen die een aantekening zijn over iets dat in de toekomst moet worden gedaan, maar om de een of andere reden nu niet kan worden gedaan. Dit is een goede gewoonte, maar dergelijke opmerkingen moeten regelmatig worden herzien om irrelevante opmerkingen te verwijderen en rommel te voorkomen.

    Een voorbeeld zou zijn:

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

    Hier merken we op dat we een vergelijking moeten toevoegen van de gebruiker die de downloadbewerking uitvoert (wiens ID we uit de beveiligingscontext zullen halen) met degene die de opslagbewerking heeft uitgevoerd.

  • Versterkende opmerkingen - Opmerkingen die het belang benadrukken van een omstandigheid die op het eerste gezicht onbeduidend lijkt.

    Beschouw als voorbeeld een deel van een methode die een testdatabase vult met enkele scripts:

    
    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-opmerkingen — Opmerkingen die de API beschrijven voor bepaalde functionaliteit. Er zijn waarschijnlijk de nuttigste opmerkingen, aangezien de gedocumenteerde API veel gemakkelijker is om mee te werken. Dat gezegd hebbende, ze kunnen ook verouderd zijn, net als elk ander type opmerking. Vergeet dus nooit dat de belangrijkste bijdrage aan documentatie niet wordt geleverd door opmerkingen, maar door goede code.

    Hier is een voorbeeld van een vrij gebruikelijke methode om een ​​gebruiker bij te werken:

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

Slechte opmerkingen

  • mompelende opmerking - Opmerkingen die meestal in haast worden geschreven en waarvan de betekenis alleen begrijpelijk is voor de ontwikkelaar die ze heeft geschreven, aangezien alleen hij of zij de genuanceerde situatie waarneemt waarnaar de opmerking verwijst.

    Overweeg dit voorbeeld:

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

    Wie laadt deze instellingen? Zijn ze al geladen? Moet deze methode uitzonderingen opvangen en standaardinstellingen laden? Er rijzen te veel vragen die alleen kunnen worden beantwoord door zich te verdiepen in een onderzoek naar andere delen van het systeem.

  • Overbodige opmerkingen — Opmerkingen die geen semantische lading hebben, aangezien wat er gebeurt in een bepaald gedeelte van de code overduidelijk is. Met andere woorden, de opmerking is niet gemakkelijker te lezen dan de code.

    Laten we een voorbeeld bekijken:

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

    Wat is het nut van dergelijke opmerkingen? Alles wat ze uitleggen is al volkomen duidelijk.

  • Onbetrouwbare opmerkingen — Opmerkingen die onwaar en alleen maar misleidend zijn (desinformatie). Hier is er bijvoorbeeld een.

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

    Wat is er mis met deze opmerking? Het feit dat het een beetje tegen ons liegt, in die zin dat de verbinding wordt verbroken als isNotUsing niet waar is, niet andersom, zoals de opmerking ons informeert.

  • Verplichte opmerkingen — Opmerkingen die als verplicht worden beschouwd (bijv. Javadoc-commentaar), maar die zich in feite soms buitensporig opstapelen en onbetrouwbaar en onnodig zijn (u moet nadenken of deze opmerkingen echt nodig zijn).

  • Voorbeeld:

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

    Zou je kunnen begrijpen wat de methode doet zonder deze opmerkingen? Hoogstwaarschijnlijk, ja, dus opmerkingen worden hier zinloos.

  • Logboekopmerkingen — Opmerkingen die soms aan het begin van een module worden toegevoegd elke keer dat deze wordt bewerkt (zoiets als een wijzigingslogboek).

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

    Deze benadering was ooit gerechtvaardigd, maar met de komst van versiebeheersystemen (bijvoorbeeld Git) werd het een onnodige rommel en complicatie van de code.

  • Auteurschapsopmerkingen — Opmerkingen waarvan het doel is om de persoon aan te geven die de code heeft geschreven, zodat u contact met hem/haar kunt opnemen en kunt bespreken hoe, wat en waarom, bijvoorbeeld:

    
    * @author Bender Bending
    

    Nogmaals, versiebeheersystemen onthouden precies wie wanneer een stukje code heeft toegevoegd, dus deze aanpak is overbodig.

  • Uitgecommentarieerde code — Code die om de een of andere reden is uitgecommentarieerd. Dit is een van de slechtste gewoonten, want wat er gebeurt, is dat je iets uitcommentaart en het vergeet, en dat andere ontwikkelaars gewoon niet de moed hebben om het te verwijderen (wat als het tenslotte iets waardevols is?).

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

    Als gevolg hiervan stapelt de uitgecommentarieerde code zich op als afval. Laat in geen geval zo'n code achter. Als je het echt nodig hebt, vergeet dan het versiebeheersysteem niet.

  • Niet voor de hand liggende opmerkingen — Opmerkingen die iets op een buitengewoon gecompliceerde manier beschrijven.

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

    Een opmerking moet de code uitleggen. Het zou zelf geen uitleg nodig hebben. Dus wat is hier mis? Wat zijn "filterbytes"? Waar gaat die "+ 1" over? Waarom precies 300?

Als je al hebt besloten om reacties te schrijven, volgen hier een paar tips:
  1. Gebruik stijlen die gemakkelijk te onderhouden zijn: het onderhouden van stijlen die te chique en exotisch zijn, is vervelend en tijdrovend.
  2. Gebruik geen end-of-line commentaren die verwijzen naar enkele regels: het resultaat is een grote stapel commentaren. Bovendien is het moeilijk om voor elke regel een zinnig commentaar te bedenken.
  3. Probeer bij het opstellen van een opmerking de vraag "waarom" te beantwoorden, niet "hoe".
  4. Vermijd beknopte informatie. Zoals ik hierboven al zei, hebben we geen uitleg nodig voor een opmerking: de opmerking zelf is de uitleg.
  5. U kunt opmerkingen gebruiken om eenheden en waardebereiken te noteren.
  6. Plaats opmerkingen dicht bij de code die ze beschrijven.
Ten slotte wil ik u er nog steeds aan herinneren dat de beste opmerking geen opmerking is, maar eerder het gebruik van bekwame naamgeving in uw hele toepassing. In de regel zullen we meestal met bestaande code werken, deze onderhouden en uitbreiden. Het is veel handiger als deze code gemakkelijk te lezen en te begrijpen is, aangezien slechte code een obstakel is. Het is alsof je een sleutel in het spel gooit, en haast is zijn trouwe metgezel. En hoe meer slechte code we hebben, hoe meer de prestaties dalen. Dit betekent dat we van tijd tot tijd moeten refactoren. Maar als je vanaf het begin code probeert te schrijven die ervoor zorgt dat de volgende ontwikkelaars je niet willen vinden en vermoorden, dan hoef je het niet zo vaak te herstructureren. Maar het zal nog steeds nodig zijn, aangezien de voorwaarden en vereisten van het product voortdurend veranderen met de toevoeging van nieuwe afhankelijkheden en verbindingen. Nou, ik denk dat dat alles voor mij was vandaag. Bedankt aan iedereen die tot hier heeft gelezen :)
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION