Udvikler: Drop de alt for lange navne!

Er du enig i, at et navn som Version2FiskerEfterUdviklerFlamewarArtikel er lige i overkanten, eller er det bedre med beskrivende navne til metoder og variable?

Navngivning af variable og metoder kan være en smertefuld proces, hvor gode intentioner ender med kode, der enten er som at læse 'Krig og Fred' eller monteringsvejledningen til et billigere-end-Ikea-møbel. Altså enten alt for lange eller alt for korte.

Det er især de lange navne, der har fået udvikler Robert Nystrom til i et blogindlæg at lange ud efter programmørernes dårlige navngivningsvaner.

Problemet med alt for lange navne er ifølge Robert Nystrom, at de overskygger, hvad der rent faktisk sker i koden. Når navnet på en variabel eller parameter bliver meget langt og samtidig fodres til metodekald, der er tilsvarende lange, så drukner informationen om, hvad koden gør, i information om parametre og metoder.

Han har derfor opstillet fire regler for at skære ned på længden af navne.

1. Udelad det åbenlyse

Når man koder i typefaste sprog, er man sjældent i tvivl om, hvilken type en variabel er eller kan relativt hurtigt finde frem til det. Derfor er der ingen grund til at benytte sig af den slags notation, der ligner et spøgelse fra de sprog hvor ungarsk notation blev brugt.

Din kode behøver altså ikke indeholde typeangivelser i variabelnavne.

String nameString;
// Bør i stedet skrives som:
String name;

Tilsvarende behøver et metodenavn ikke indeholde de typer, metoden tager som parametre, da det er lettere bare at læse den liste af parametre, der følger lige efter.

2. Udelad det, der ikke er unikt

Navne skal blot være tilstrækkeligt unikke til at sikre, at man kan gennemskue, hvad de bruges til. Det bør ikke være en komplet beskrivelse af deres funktion.

finalBattleMostDangerousBossMonster;
// Kunne du sikkert skrive som:
boss;

Hvis du skal navngive en henvisning til boss-monstret i dit spil, så er det sikkert ikke nødvendigt at skrive, at der er tale om et monster, eller at bossen optræder i den sidste kamp. Det ville kun være relevant, hvis der var bosser, der ikke var monstre, eller en boss, der ikke optrådte i den sidste kamp.

Som udgangspunkt bør man ifølge Robert Nystrom skære helt ind til benet, og så eventuelt ændre det, hvis man får brug for det senere. De fleste udviklingsværktøjer gør det forholdsvis enkelt at rette.

3. Udelad det, der kan læses af sammenhængen

Et klassisk problem er at navngive variable i en klasse med information, der allerede fremgår af klassens navn.

class Version2Artikel {
  string version2ArtikelForfatter;
  void udgivVersion2Artikel { ... }
}
// Bliver bedre som:
class Version2Artikel {
  string forfatter;
  void udgiv { ... }
}

Da man vil læse navnene i en sammenhæng, så er det ikke nødvendigt at gentage informationen i alle navnene. Helt i den objektorienterede ånd nedarver de deres kontekst, som hjælper med at aflæse dem.

4. Udelad overflødige ord

Den fjerde regel er sådan set blot den generelle version af de første tre, men den henviser også til nogle af de ord, der er almindelige at hægte på navne. Ud over typer, som i den første regel, så kan det være nogle etablerede beskrivelser af funktionalitet, som ikke altid er nødvendige.

Det gælder ord som 'manager', 'handler', 'value', 'data' og flere andre, som bliver overflødige, fordi det giver sig selv, hvad der foregår, når man kan læse koden.

Hvis man vil bruge beskrivende navne, bør man også være nærig med ordene. Kan et ord skæres væk, så bør man gøre det.

class DejligtKoldeFredagsØlBegivenhed {
  boolean erFredagsbarenÅben() { . . . }
}
// Kan skrives som
class FredagsØl {
  boolean Åben() { . . . }
}

Hvad mener du, bliver det for meget af det gode, hvis navnene ikke forklarer, hvad de bliver brugt til, når de dukker op 50 linjer senere? Eller har journalistens brug af danske ord til metoder gjort dig for rasende til at forholde dig til længden af navne?

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Kommentarer (26)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Allan S. Hansen

Som altid er det jo ekstremt svært at finde en middelvej der vil passe alle udviklere, alle guruer og alle bloggere m.fl. over hele verden......

Så derfor kan man altid opstille ekstremer for synspunkter for enhver holdning.
Der går sikkert ikke lang tid før en anden person laver et tilsvarende blog skriv om at det er for ringe at udviklere bruger for korte navne og at man ikke må bruger "x" og "y" og og at man ikke skal kalde sin klasse for "FredagsØl" hvis den også indeholder sodavand.

Derfor vil jeg altid tage sådanne posts som dette med et kæmpe gran salt fordi ekstermer er ikke interessante i hverdagens arbejde hvor sandsynligvis de fleste af os går efter at finde en pragmatisk middelvej hver gang der kodes.
Ekstremerne er kun interessante for blog posts.

Tobias Tobiasen

Er det øllen eller fredagsbaren der er åben?
erFredagsbarenÅben() {} er klart at man spørger om fredagsbaren er åben.

class FredagsØl {  
  boolean Åben() { . . . }  
}

Åbner det øllen? Eller checker det om øllen er åben? Eller det åbner måske baren?

Niels Buus

Så må den hedde FredagsØlBar...

Og derudover skal man også passe på at hardcode for mange ting, da det kan gøre koden ufleksibel. Derfor kan man omdøbe klassen til UgeØlBar, og give en ugedag med ind.

class WeekBeerBar  
  attr_reader :opening_weekday  
   
  def initialize(opening_weekday)  
    @opening_weekday = opening_weekday  
    @beertap = BeerTap.new("beer://#{beer_source}", stream: true)  
  end  
   
  def beer_source  
     ENV.fetch("BEER_SOURCE", "tuborg.dk")  
  end  
   
  def open  
    if opening_weekday == Date.today.weekday?  
      @beertap.open  
    else  
      fail BingetimeError.new("Beer resource is currently unavailable.")  
    end  
  end  
end
Troels Henriksen

Åbner det øllen? Eller checker det om øllen er åben? Eller det åbner måske baren?

Man kan tilnærmelsesvist se på typen at det må være anden mulighed, da der er tale om en prædikatfunktion. En anden kompakt måde at udtrykke disse forskelle på er at gøre som i Scheme, og sætte udråbstegn efter funktioner med side-effekter, og spørgsmålstegn efter prædikater. Således ville man have 'åben?' og 'åben!' som funktionsnavne. Det opklarer selvfølgelig ingen flertydighed mellem øller og barer.

En anden måde at slippe afsted med korte navne er at være konsistent i deres brug. Jeg arbejder på en oversætter, og i denne navngiver jeg næsten altid variable af typen 'Type' som 't' og variable af Typen 'Exp' som 'e'. Hvis jeg har flere 'Exp'-værdier (f.eks. når jeg håndterer en binær operation) kan jeg så have 'e1' og 'e2', og hvis jeg beregner typen af disse, vil jeg kalde dem 't1' og 't2'. Kompakt men rimeligt forståeligt, især hvis funktionerne holdes korte.

Peter Valdemar Mørch

Eksemplet er Ruby? Der er forskel på typestærke og -svage sprog her.

For eclipse f.eks. kan fint finde præcis de rigtige "open" instanser for netop java klasse X, men det kan man ikke så let i python, perl, ruby eller javascript. Så i de sprog ville jeg vælge noget lidt mere søgbart

Henrik K. Jensen

For mig er scope en vigtig parameter når jeg navngiver variable. Variable med et mere globalt scope har normalt et mere beskrivende navn, hvorimod en lokal variabel ofte vil være et eller to tegn. Der vil dog altid være undtagelser, f.eks. hvis der er mange lokale variable, så vil de have mere beskrivende navne.

Bent Jensen

konsistent og dokumentation af denne må være det rigtige.

Hvis man dokomentere sin stil, hvis man ikke bruger en "standart".
Samt alle som normalt bruger, og som læser koderne, forstår og gør det på den samme måde. Så må man vel bruge den metode man vil.

Så bare alle som arbejder på et projekt er enig om stil, eller en forøget dokumentation hvis ikke. Også på input/output hvis ellers funktion virker.

Synes nu selv lidt mere komentar tekst, er bedre end hvordan længten og formater er på variabler, så man også selv kan huske det efter nogen år.

Kim Madsen
TDrinkBeer = class  
public  
  constructor Create;  
end;  
   
constructor TDrinkBeer.Create;  
begin  
  inherited;  
  // Make sure we drink some beer in end of week.  
  Scheduler.Schedule(  
    function DrinkBeer(const AEvent:IkbmMWScheduledEvent):boolean;  
    begin  
         ShowMessage('DRINK BEER NOW!');  
         Result:=true;  
    end;  
   ).AtWeekdays([mwwdWednesday,mwwdThursday,mwwdFriday])  
    .EveryHour(0.5)  
    .Synchronized.  
    .Activate;  
   
  Scheduler.Schedule(  
    function Recouperate(const AEvent:IkbmMWScheduledEvent):boolean;  
    begin  
         sleep(1000);  
         Result:=true;  
    end;  
   ).AtWeekdays([mwwdSaturday,mwwdSunday,mwwdMonday])  
    .Activate;  
   
  Scheduler.Schedule(  
    function Celebrate(const AEvent:IkbmMWScheduledEvent):boolean;  
    begin  
         ShowMessage('GOD JUL!');  
         Result:=true;  
    end;  
   ).EveryYear(1)  
    .Starting(TkbmMWDateTime.Create('2016-12-24T12:00:00GMT'))  
    .Syncronized  
    .Activate;  
end;

Dette ville være mit forslag til en ordentlig ølrytme med respekt for traditioner og fysisk velvære :)

mvh
Kim Bo Madsen

Jesper Høgh

For mig er scope en vigtig parameter når jeg navngiver variable. Variable med et mere globalt scope har normalt et mere beskrivende navn ...


Helt enig. Det optimale må vel være at navngive, så også man selv kan gennemskue det.

Altså <class>.<var/func>.

Mange variable kan naturligvis benævnes værdi, men de præciseres så ved om det er fra overskud eller underskud objektet, de tilgås, altså:

overskud.værdi
eller
underskud.værdi

Problemet er vel egentlig mest relevant i de rigtige programmeringssprog, såsom c og ... øhh - nåhr jo: assembler.

Rune Larsen

"Åbner det øllen? Eller checker det om øllen er åben? Eller det åbner måske baren?"

Man kan tilnærmelsesvist se på typen at det må være anden mulighed, da der er tale om en prædikatfunktion.

Nej, selvom funktionen returnerer boolean kan du ikke se om det er en isXyz() eller doXyz(). Mange doXyz() returnerer information om hvordan operationen forløb i en boolean. Fx javas Set.add.

Baldur Norddahl

Mange doXyz() returnerer information om hvordan operationen forløb i en boolean. Fx javas Set.add.

Javas collection API er gammelt. Mere moderne er at returnere collectionen så at du kan chaine operationer. Eller fordi det er et immutable collection, så returværdien er den ændrede udgave.

Hvis det endelig skal være, så er "true" og "false" forskelligt fra "Success" og "Failure" (fra Scala). Medmindre man arbejder i C, så er det dovenskab at misbruge boolean til noget det ikke er.

Rust har optimeret brugerdefinerede typer, således at hvis der kun er to mulige typer, hvoraf den ene er uden indre værdi, så bliver denne optimeret til værdien "null" i maskinkode. Dvs. det er gratis at returnere success på denne måde.

Troels Jensen

Det er henad scope-navngivning, men jeg navngiver efter hvor langt væk variablerne skal bruges.

Indenfor samme funktion synes jeg det er fint med helt korte navne eller endda akronymer. Det vigtige er at de ikke ser for ens ud. Loopvariabler kan sagtens bare være eet bogstav.

Hvis variabler skal bruges på tværs af funktioner hiver jeg camelcasen og småsætningsnavene frem.

Torben Mogensen Blogger

Eksempler på rigtig gode variabelnavne er I, l og O eller kombinationer af disse. For eksempel kan man have meget morskab af denne løkke:

for (i=l; i<ll; i++) { ... }

Derudover bør man i samme program have funktionerne initializeColor(), initialiseColour(), initializeColour(), og initialiseColor(), som alle betyder og gør noget forskelligt, men har samme type. Tilføj evt. InitializeColor(), InitColor(), initializeColors(), osv. til sættet.

Peter Valdemar Mørch

> Det er godt vi har autocomplete i vores IDE...:)

Det er jo præcis pga autocomplete man ender med en variabel der hedder f.eks.:

beerDecoratorFactoryInitializerHandlerFactory

eller denne enum fra VSphere's API:

VirtualMachineDeviceRuntimeInfoVirtualEthernetCardRuntimeStateVmDirectPathGen2InactiveReasonOther

Log ind eller Opret konto for at kommentere