lördag 14 februari 2009

G31-G36

Nu avslutar vi de generella reglerna (där av G). Det har varit tungt att ta sig igenom dem. Det som återstår efter detta inlägg är namnsättning och tester (kanske javareglerna också men de känns lite för specifika för min smak).

G31: Dolt beroende av tid/ordning
Ofta är metodanrop beroende av den ordning de anropas. Att döpa metoderna till first, second och third är kanske inte riktigt idealiskt för att hjälpa läsaren att förstå vilken ordning som anropen måste göras. Dessutom är det inget som faktiskt hindrar en programmerare från att anropa second före first. Bättre att first returnerar ett objekt av någon typ som second kan använda och att second i sin tur returnerar ett objekt som thrid tar som argument. Dessutom behöver man inte hänvisa till någon konvention (G27) utan tvingar användaren att följa anropsordningen, eller något som i alla fall tvingar den anropande klassen att se till att ha sina saker i ordning.

G32: Var inte godtycklig
Ha en anledning till varför du strukturerar din kod på ett speciellt sätt och se sedan till att koden på ett tydligt sätt reflekterar den strukturen. Om du i kod lyckas komunicera varför dina val är som de är kommer andra utvecklare följa ditt exempel. Om din strukturen är otydlig och svävande kommer känna att de kan göra på andra och för dem bättre sätt.

G33: Kapsla in gränsfall
Gränsfall är svåra att hålla koll på. Se därför till att de enbart hanteras på ett ställe och inte sprider sig som en pest genom koden. Ett enkelt exempel är en koll där första objektet i en lista inte ska kolla om det finns något tidigare objekt i listan. Siffran noll som värde på att det är den första ska vara dold bakom en variabel tex döpt till firstObject (G25). Metoden som faktiskt vet att det är skillnad på första och andra objektet i listan bör också bara finnas på ett ställe. Övrig kod ska inte behöva veta att det är skillnad i hanteringen, det är en implementationsdetalj.

G34: Funktioner går enbart en abstraktionsnivå ner
Denna har en del med G6 att göra och jag återanvänder bilexemplet. Bilen står still i en korsning och ska svänga vänster. Metoden för att hantera denna situaion ska öka farten och styra åt vänster och sedan när tillräklig riktningsförändring skett sluta svänga. Det är alltså tre anrop till tre andra metoder som tar hand om detaljerna med att bestämma bränsleblandning, mängd bränsle som ska sprutas in i cylindrarna och annat som faktiskt behövs för att bilen ska fungera. En enkel regel för att se när man går ner en abstraktionsnivå i koden är att man använder val eller snurror. Det som händer inne i valet eller snurran är oftast på en annan abstraktionsnivå och hör där med hemma i en egen metod och håller där med den första metoden kvar på "sin" abstraktionsnivå. Detta tillsammans med G30 gör det betydligt svårare att skriva komplicerad kod.

G35: Håll konfigurerbart data på hög abstraktionsnivå
Konfiguration är viktigt, att gömma den typen av information långt ner är inte helt lyckat. Den ska vara enkel att hitta och enkel att förändra. Att gömma konfigurerbart data där det faktiskt används innebär att läsaren får leta länge efter hur man ändrar tex en ipadress till en webservice.

G36: Undvik beroenden på beroende
I normalfallet vill vi att vår kod ska känna till så lite som möjligt om annan kod. Betänk följande metod i en klass:

public void aMethod(){
a.getB().getC.doSomething()
}

Den här klassen har en medlemsvariabel som heter a. Vår klass känner till att a har en metod som heter getB och det är helt normalt. Men att klassen känner till att det som returneras från getB() också har en metod som heter getC() och att man på den kan anropa doSomething() är ganska illa. Varför det är dåligt. Om man nu skulle vilja förändra getA() så att den i stället för att returnera ett objekt som har en metod som heter getB() returnerar ett objekt som inte har den metoden så måste du även ändra på alla ställen där man gjort en anropskedja som ovan. Hur löser man problemet då? I första steget använder vi G19 (förklarande variabler) så att det går att se vad getA() och getB() och getC() returnerar för något. Nästa steg är att undvika duplicering och bryta ut anropskedjan så att den bara existerar på ett ställe. Det tredje steget är lite svårare men det går ut på är att steg för steg korta ner kedjan genom att i stället för att anropa getA() anropa doSomething() i som i sin tur anropar den del av kedjan som är kvar och sedan upprepar man tills kedjan inte längre existerar. Ser att texten blir knölig att läsa så jag skriver ett kodexempel:

// i vår klass
public void aMethod()
a.doSomething();
}

// i klassen som som objektet a kommer ifrån
public void doSomething(){
b.getC().doSomething();
}

Här har jag tagit bort ett steg ur anropskedjan i första exemplet. Det kan dock bli många metoder i en del klasser om de ska ta hand om anrop den här vägen så det kan kan ibland vara idé att implementera speciella klasser som får vara en punkt i applikationen som kanske har lite mer kunskap om ett antal klasser än de borde ha.

Inga kommentarer:

Skicka en kommentar