Udvikler: Drop de alt for lange navne!

17. juni 2016 kl. 15:3526
Er du enig i, at et navn som Version2FiskerEfterUdviklerFlamewarArtikel er lige i overkanten, eller er det bedre med beskrivende navne til metoder og variable?
Artiklen er ældre end 30 dage
Manglende links i teksten kan sandsynligvis findes i bunden af artiklen.

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.

Artiklen fortsætter efter annoncen

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

  1. String nameString;
  2. // Bør i stedet skrives som:
  3. 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.

  1. finalBattleMostDangerousBossMonster;
  2. // Kunne du sikkert skrive som:
  3. 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.

Artiklen fortsætter efter annoncen

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.

  1. class Version2Artikel {
  2. string version2ArtikelForfatter;
  3. void udgivVersion2Artikel { ... }
  4. }
  5. // Bliver bedre som:
  6. class Version2Artikel {
  7. string forfatter;
  8. void udgiv { ... }
  9. }

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.

  1. class DejligtKoldeFredagsØlBegivenhed {
  2. boolean erFredagsbarenÅben() { . . . }
  3. }
  4. // Kan skrives som
  5. class FredagsØl {
  6. boolean Åben() { . . . }
  7. }

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?

26 kommentarer.  Hop til debatten
Denne artikel er gratis...

...men det er dyrt at lave god journalistik. Derfor beder vi dig overveje at tegne abonnement på Version2.

Digitaliseringen buldrer derudaf, og it-folkene tegner fremtidens Danmark. Derfor er det vigtigere end nogensinde med et kvalificeret bud på, hvordan it bedst kan være med til at udvikle det danske samfund og erhvervsliv.

Og der har aldrig været mere akut brug for en kritisk vagthund, der råber op, når der tages forkerte it-beslutninger.

Den rolle har Version2 indtaget siden 2006 - og det bliver vi ved med.

Debatten
Log ind eller opret en bruger for at deltage i debatten.
settingsDebatindstillinger
26
22. juni 2016 kl. 14:08

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

25
21. juni 2016 kl. 13:51

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

24
21. juni 2016 kl. 09:44

I artiklen: "String nameString; ... // Bør i stedet skrives som: ... String name;"

At tilføje en "type" til variablens navn kan være nødvendig hvis der er flere repræsentationer af samme information, feks. WeekdayNumber og WeekdayString.

22
20. juni 2016 kl. 14:55

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.

21
20. juni 2016 kl. 13:07

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.

17
19. juni 2016 kl. 22:02

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.

16
19. juni 2016 kl. 20:49

Der er nu altså kun to:

Cache-invalidering, navngivning og off-by-one errors

:-)

14
18. juni 2016 kl. 15:48

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å .<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.

13
18. juni 2016 kl. 13:08

  1. TDrinkBeer = class
  2. public
  3. constructor Create;
  4. end;
  5.  
  6. constructor TDrinkBeer.Create;
  7. begin
  8. inherited;
  9. // Make sure we drink some beer in end of week.
  10. Scheduler.Schedule(
  11. function DrinkBeer(const AEvent:IkbmMWScheduledEvent):boolean;
  12. begin
  13. ShowMessage('DRINK BEER NOW!');
  14. Result:=true;
  15. end;
  16. ).AtWeekdays([mwwdWednesday,mwwdThursday,mwwdFriday])
  17. .EveryHour(0.5)
  18. .Synchronized.
  19. .Activate;
  20.  
  21. Scheduler.Schedule(
  22. function Recouperate(const AEvent:IkbmMWScheduledEvent):boolean;
  23. begin
  24. sleep(1000);
  25. Result:=true;
  26. end;
  27. ).AtWeekdays([mwwdSaturday,mwwdSunday,mwwdMonday])
  28. .Activate;
  29.  
  30. Scheduler.Schedule(
  31. function Celebrate(const AEvent:IkbmMWScheduledEvent):boolean;
  32. begin
  33. ShowMessage('GOD JUL!');
  34. Result:=true;
  35. end;
  36. ).EveryYear(1)
  37. .Starting(TkbmMWDateTime.Create('2016-12-24T12:00:00GMT'))
  38. .Syncronized
  39. .Activate;
  40. end;
Dette ville være mit forslag til en ordentlig ølrytme med respekt for traditioner og fysisk velvære :)

mvh Kim Bo Madsen

12
17. juni 2016 kl. 22:59

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.

11
17. juni 2016 kl. 22:20

Bla bla bla bla bla bla bla fredagsbar bla bla øl bla bla bla bla....

10
17. juni 2016 kl. 20:59

Navngivning og off by 1 fejl har altid været de tre sværeste problemer.

9
17. juni 2016 kl. 19:19

Skulle det ikke være en erÅben() fx? For at undgå forvirring med en åbnende funktion?

8
17. juni 2016 kl. 19:06

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.

7
17. juni 2016 kl. 18:12

Du har principielt ret ang. YAGNI, men måske jeg sidder på noget domæneviden om at organisationen er alkoholisk anlagt...

6
17. juni 2016 kl. 17:57

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

4
17. juni 2016 kl. 17:16

Å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.

3
17. juni 2016 kl. 17:14

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.

  1. class WeekBeerBar
  2. attr_reader :opening_weekday
  3.  
  4. def initialize(opening_weekday)
  5. @opening_weekday = opening_weekday
  6. @beertap = BeerTap.new("beer://#{beer_source}", stream: true)
  7. end
  8.  
  9. def beer_source
  10. ENV.fetch("BEER_SOURCE", "tuborg.dk")
  11. end
  12.  
  13. def open
  14. if opening_weekday == Date.today.weekday?
  15. @beertap.open
  16. else
  17. fail BingetimeError.new("Beer resource is currently unavailable.")
  18. end
  19. end
  20. end

2
17. juni 2016 kl. 16:09

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

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

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

1
17. juni 2016 kl. 15:46

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.