# Zelfdocumenterende Code
Zelfdocumenterende code is een programmeerprincipe waarbij de code zelf voldoende informatie bevat om gemakkelijk te begrijpen en te onderhouden te zijn. Een krachtige techniek om zelfdocumenterende code te schrijven, is het gebruik van duidelijke namen voor variabelen, functies en methoden. Laten we dit principe toepassen op enkele refactoring-oefeningen.
# Overzicht van Technieken
# Structureel
# Functies
Verplaats code naar een functie voor duidelijkheid en hergebruik. Volgende code is correct, maar wat dit precies doet is niet duidelijk.
let width = (value - 0.5) * 16;
We schrijven beter de code in een functie met een duidelijke naam:
let width = emToPixels(value);
function emToPixels(ems) {
return (ems - 0.5) \* 16;
}
2
3
4
5
Vervang voorwaardelijke expressies door een functie voor meer duidelijke code.
if (!el.offsetWidth || !el.offsetHeight) {
// je code hier
}
2
3
Door dit in een functie te schrijven kunnen we verduidelijken wat deze lijn doet.
function isVisible(el) {
return el.offsetWidth && el.offsetHeight;
}
2
3
Een andere mogelijkheid is om de expressies door variabelen te vervangen voor duidelijkheid, vooral in specifieke algoritmen.
let isVisible = el.offsetWidth && el.offsetHeight;
if (!isVisible) {
// je code hier
}
2
3
4
Een ander voorbeeld is:
return a * b + c / d;
Dit wordt dan:
let multiplier = a * b;
let divisor = c / d;
return multiplier + divisor;
2
3
4
# Klasses en modules
Definieer duidelijke klassen- en module-interfaces voor beter begrip van het gebruik.
class Box {
setState(state) {
this.state = state;
}
getState() {
return this.state;
}
}
2
3
4
5
6
7
8
9
Hierin is niet direct duidelijk hoe we deze klasse moeten gebruiken, we kunnen dit verduidelijken door te herschrijven naar:
class Box {
open() {
this.state = "open";
}
close() {
this.state = "closed";
}
isOpen() {
return this.state === "open";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Groeperen
Groepeer code logisch om relaties tussen verschillende delen aan te geven.
let foo = 1;
bar(foo);
quux(foo);
blah();
xyz();
baz(1337);
2
3
4
5
6
7
8
Maar beter is dus:
let foo = 1;
bar(foo);
quux(foo);
blah();
xyz();
baz(1337);
2
3
4
5
6
7
8
# Zuivere functies
Geef de voorkeur aan pure functies voor een duidelijker begrip en vermijd afhankelijkheid van de status.
Wat is een zuivere functie? Als je een functie aanroept met dezelfde parameters en deze altijd dezelfde uitvoer produceert, is het waarschijnlijk een zogenaamde “pure” functie. Dit betekent dat de functie geen neveneffecten mag hebben of afhankelijk mag zijn van toestand - zoals tijd, objecteigenschappen, Ajax, enz.
# Benaming
Het benoemen van functies en variabelen is vaak niet al te moeilijk, maar er zijn enkele eenvoudige regels die je kunt volgen:
# Functiebenamingen
- Vermijd het gebruik van vage woorden zoals “handle” of “manage”:
handleLinks(),manageObjects(). Wat doen deze functies precies? - Gebruik werkwoorden in de actieve vorm:
cutGrass(),sendFile()- functies die actief iets uitvoeren. - Geef de retourwaarde aan:
getMagicBullet(),readFile(). Dit is niet altijd mogelijk, maar het is nuttig waar het zinvol is. - Talen met sterke typering kunnen typehandtekeningen gebruiken om retourwaarden aan te geven.
# Variabelennamen
Bij variabelen zijn hier twee goede vuistregels:
- Geef eenheden aan: als je numerieke parameters hebt, kun je de verwachte eenheid opnemen. Bijvoorbeeld,
widthPxin plaats vanwidthom aan te geven dat de waarde in pixels is in plaats van in een andere eenheid. - Gebruik geen afkortingen:
aofbzijn geen acceptabele namen, behalve voor tellers in lussen.
let widthPx = ...
# Conventies
Volg vastgestelde benamingconventies voor consistentie.
let element = getElement();
In plaats van
let node = getElement();
# Fouten
Gebruik zinvolle foutmeldingen die het probleem beschrijven en relevante gegevens verstrekken.
throw new Error("Undefined is not an object!");
# Syntaxis
Vermijd syntactische trucs voor een betere leesbaarheid van de code.
imTricky && doMagic();
Wordt dan:
if (imTricky) {
doMagic();
}
2
3
Gebruik constanten in plaats van magische waarden.
const MEANING_OF_LIFE = 42;
const PI = 3.14;
2
Vermijd boolean-vlaggen voor duidelijkere code.
myThing.setData({ x: 1 }, true);
Je moet al deze methode gaan bekijken om te weten wat dit doet, beter kunnen we schrijven:
myThing.mergeData({ x: 1 });
Maak gebruik van ingebouwde methodes om de communicatie van de codeintentie te verbeteren.
var ids = [];
for (var i = 0; i < things.length; i++) {
ids.push(things[i].id);
}
2
3
4
Kan ook geschreven worden als:
let ids = things.map(function (thing) {
return thing.id;
});
2
3
# Anti-patronen
Wees voorzichtig met het extraheren van functies uitsluitend vanwege de lengte, omdat dit de begrijpelijkheid van de code kan beïnvloeden.
Forceer technieken niet als ze niet geschikt lijken; er is geen one-size-fits-all benadering.