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.
lördag 14 februari 2009
fredag 13 februari 2009
G26 - G30
Inte många kvar på G efter den här...
G26: Var exakt
Den här handlar om att inte slarva och göra halvhjärtade val. Om du tex fyller en ArrayList med ett antal värden och sedan sak returnera denna så bör du fundera på om returntypen faktisk ska vara ArrayList. Kanske det är bättre att returnera bastypen List. Det kanske inte är så att du vill att en anropande klass ska kunna modifiera listan så en enumeration kanske är bättre. Om du returnera en List måste du implementera din metod för att faktiskt kunna hantera att listan ändras. Om du returnera ArrayList så måste du ha en anledning till att visa den implementationsdetaljen. Så fundera på hur du vill att din metod ska användas och tänk igenom de konsekvenser dina val får.
G27: Struktur över konvention
Dokument med konventioner kan vara bra ibland. Men att hindra de andra utvecklarna (och sig själv) från att missbruka koden är bättre. Får man inte anropa databaslagret från GUI-lagret se då till att det inte går genom att göra klasserna onåbara från GUI-lagret. Om dina dataproviders måste ha en transaktion kontrollera då det i koden så att de som försöker att använda dem får stora fula felmeddelanden om transaktionen inte är på plats. Struktur vinner över konvention alla gånger av alla (nästan).
G28: Dölj boolska uttryck
Betänk följande metod:
public void MyMethod(){
if( a == running & b <>
// do something
}
}
och jämnför med följande:
public void MyMethod(){
if(isTemperatureSafe()){
// do something
}
}
Boolska uttryck blir snabbt oläsbara och även om de är enkla och där med läsbara så är syftet av variable > 0 självklart alla gånger. Vad betyder det att värdet är större än noll? Kanske hade varit bättre med att ha en metod som faktiskt talar om vad vi testar.
G29: Undvik negativa booska uttryck
Vi människor har lite svårare att ta till oss negativa uttryck. Inte sant är svårare att förstå än falskt. Att använda de små språkkonstrukten som utropstecken (!) för att markera att uttrycket är falskt gör inte saken lättare. Försök att hitta uttryck som inte behöver använda negationer och dölj uttroptecken bakom metoder som låter dig skriva i klartext.
G30: Funktioner bör göra en sak
Denna är viktig. En metod ska göra en av två saker. Ta ett beslut eller utföra EN uppgift, till exempel populera ett objekt med värden från ett argument. Metoder som tar flera beslut är svårare att förstå. Metoder som tar ett beslut på om om argumentet är användbart, sedan populerar ett objekt och sedan tar ett annat beslut på om objektet är bra mycket svårare att förstå. Kanske så att man kan argumentera för att utvecklare är smartare än genomsnittsbefolkningen men det är förmodligen bättre ekonomi i att låta programmerare syssla med komplexa problem än komplex kod.
G26: Var exakt
Den här handlar om att inte slarva och göra halvhjärtade val. Om du tex fyller en ArrayList med ett antal värden och sedan sak returnera denna så bör du fundera på om returntypen faktisk ska vara ArrayList. Kanske det är bättre att returnera bastypen List. Det kanske inte är så att du vill att en anropande klass ska kunna modifiera listan så en enumeration kanske är bättre. Om du returnera en List måste du implementera din metod för att faktiskt kunna hantera att listan ändras. Om du returnera ArrayList så måste du ha en anledning till att visa den implementationsdetaljen. Så fundera på hur du vill att din metod ska användas och tänk igenom de konsekvenser dina val får.
G27: Struktur över konvention
Dokument med konventioner kan vara bra ibland. Men att hindra de andra utvecklarna (och sig själv) från att missbruka koden är bättre. Får man inte anropa databaslagret från GUI-lagret se då till att det inte går genom att göra klasserna onåbara från GUI-lagret. Om dina dataproviders måste ha en transaktion kontrollera då det i koden så att de som försöker att använda dem får stora fula felmeddelanden om transaktionen inte är på plats. Struktur vinner över konvention alla gånger av alla (nästan).
G28: Dölj boolska uttryck
Betänk följande metod:
public void MyMethod(){
if( a == running & b <>
// do something
}
}
och jämnför med följande:
public void MyMethod(){
if(isTemperatureSafe()){
// do something
}
}
Boolska uttryck blir snabbt oläsbara och även om de är enkla och där med läsbara så är syftet av variable > 0 självklart alla gånger. Vad betyder det att värdet är större än noll? Kanske hade varit bättre med att ha en metod som faktiskt talar om vad vi testar.
G29: Undvik negativa booska uttryck
Vi människor har lite svårare att ta till oss negativa uttryck. Inte sant är svårare att förstå än falskt. Att använda de små språkkonstrukten som utropstecken (!) för att markera att uttrycket är falskt gör inte saken lättare. Försök att hitta uttryck som inte behöver använda negationer och dölj uttroptecken bakom metoder som låter dig skriva i klartext.
G30: Funktioner bör göra en sak
Denna är viktig. En metod ska göra en av två saker. Ta ett beslut eller utföra EN uppgift, till exempel populera ett objekt med värden från ett argument. Metoder som tar flera beslut är svårare att förstå. Metoder som tar ett beslut på om om argumentet är användbart, sedan populerar ett objekt och sedan tar ett annat beslut på om objektet är bra mycket svårare att förstå. Kanske så att man kan argumentera för att utvecklare är smartare än genomsnittsbefolkningen men det är förmodligen bättre ekonomi i att låta programmerare syssla med komplexa problem än komplex kod.
G21 - G25
Mer mer mer mer ... aldrig tar de slut..
G21: Förstå algoritmen
Ofta kan man se klasser och metoder där det ser ut som om något spejat skärmen med if-satser och boolska variabler. Detta kan vara ett tecken på att personen som skrivit koden har skrivit något som kanske fungerar men igentligen inte har någon större koll på vad koden faktiskt ska göra. Naturligtvis är det ofta så att man inte har riktigt koll på hur man ska lösa sin uppgift och mer eller mindre testar sig fram mot ett resultat och det är inget fel med det. Men innan du anser dig vara färdig refaktorera koden till något som faktiskt går att förstå och som faktiskt visar att du har förstått vad du gjort.
G22: Gör logiska beroenden fysiska
Denna står i lite i motsatsförhållande till G13. Den är inte helt enkel att förklara och jag stjäl exemplet i boken då jag inte lyckas komma upp med ett eget. Exemplet beskriver en utskriftfunktion som där den anropande klassen bestämmer hur många rader som ska skrivas ut på en papper genom att själv bestämma när sidbrytningarna ska ske i stället för att tala om för utskriftsklassen att den borde lägga in en sidbrytning enligt ett visst intervall. Detta innebär att utskriftklassen har ett logiskt beroende på den anropande klassen. Det hade varit bättre att skriva in detta i utskriftklassen... Ja.. inte helt enkel...
G23: Föredra arv före if/else eller switch
Jag om denna första gången i Pragmatic programmer under namnet "Don't ask, tell" och säger att man inte ska ta beslut efter vilket tillstånd en klass befinner sig i genom att fråga klassen vilket värdet variablen "type" har. I stället ska man när man skapar objektet ta beslut om vilken subtyp klassen ska vara och sedan be klassen "göra sin sak". Ser ni långa haranger med if/else som jämnför en variabel i klassen med ett antal värden är det sannolikt ett tillfälle där en polymorfisk lösning skulle vara bättre. If/else-satserna är dessutom lätta att klippa och klistra och blir där med svåra att underhålla.
G24: Följ konventioner enligt standard
Kodkonvention är inte något man skriver på varje företag. Sun har en kodkonvention för java och jag antar att Microsoft har en för C#. Följ dessa. Om ni tar in en konsult så ska de känna igen sig och om du byter arbetsplats ska du inte behöva lusläsa ett dokument för att vara säker på att man gör rätt. Om det finns frågetecken ska den existerande koden i projektet vara grund. Detta förutsätter naturligtvis att alla i projektet är rimligt vuxna och förstår att det igentligen inte spelar någon roll var man sätter krusidullparanteserna så länge alla sätter dem på samma ställe.
G25: Ersätt magiska nummer med konstanter
Visst har vi alla funderat på varför en variabel är satt till 1 eller kanske 0 eller -1 eller i värsta fall till 217? Siffran som bara står där mitt i koden utan någon förklaring som något magiskt som alla bara borde förstå. Ersätt dessa magiska konstanter i koden med riktiga konstanter som har fått bra tydliga namn. Detta gäller naturligtvis inte bara för siffror utan även för text.
G21: Förstå algoritmen
Ofta kan man se klasser och metoder där det ser ut som om något spejat skärmen med if-satser och boolska variabler. Detta kan vara ett tecken på att personen som skrivit koden har skrivit något som kanske fungerar men igentligen inte har någon större koll på vad koden faktiskt ska göra. Naturligtvis är det ofta så att man inte har riktigt koll på hur man ska lösa sin uppgift och mer eller mindre testar sig fram mot ett resultat och det är inget fel med det. Men innan du anser dig vara färdig refaktorera koden till något som faktiskt går att förstå och som faktiskt visar att du har förstått vad du gjort.
G22: Gör logiska beroenden fysiska
Denna står i lite i motsatsförhållande till G13. Den är inte helt enkel att förklara och jag stjäl exemplet i boken då jag inte lyckas komma upp med ett eget. Exemplet beskriver en utskriftfunktion som där den anropande klassen bestämmer hur många rader som ska skrivas ut på en papper genom att själv bestämma när sidbrytningarna ska ske i stället för att tala om för utskriftsklassen att den borde lägga in en sidbrytning enligt ett visst intervall. Detta innebär att utskriftklassen har ett logiskt beroende på den anropande klassen. Det hade varit bättre att skriva in detta i utskriftklassen... Ja.. inte helt enkel...
G23: Föredra arv före if/else eller switch
Jag om denna första gången i Pragmatic programmer under namnet "Don't ask, tell" och säger att man inte ska ta beslut efter vilket tillstånd en klass befinner sig i genom att fråga klassen vilket värdet variablen "type" har. I stället ska man när man skapar objektet ta beslut om vilken subtyp klassen ska vara och sedan be klassen "göra sin sak". Ser ni långa haranger med if/else som jämnför en variabel i klassen med ett antal värden är det sannolikt ett tillfälle där en polymorfisk lösning skulle vara bättre. If/else-satserna är dessutom lätta att klippa och klistra och blir där med svåra att underhålla.
G24: Följ konventioner enligt standard
Kodkonvention är inte något man skriver på varje företag. Sun har en kodkonvention för java och jag antar att Microsoft har en för C#. Följ dessa. Om ni tar in en konsult så ska de känna igen sig och om du byter arbetsplats ska du inte behöva lusläsa ett dokument för att vara säker på att man gör rätt. Om det finns frågetecken ska den existerande koden i projektet vara grund. Detta förutsätter naturligtvis att alla i projektet är rimligt vuxna och förstår att det igentligen inte spelar någon roll var man sätter krusidullparanteserna så länge alla sätter dem på samma ställe.
G25: Ersätt magiska nummer med konstanter
Visst har vi alla funderat på varför en variabel är satt till 1 eller kanske 0 eller -1 eller i värsta fall till 217? Siffran som bara står där mitt i koden utan någon förklaring som något magiskt som alla bara borde förstå. Ersätt dessa magiska konstanter i koden med riktiga konstanter som har fått bra tydliga namn. Detta gäller naturligtvis inte bara för siffror utan även för text.
onsdag 11 februari 2009
G16 - G20
Inte så mycket att säga om... G fortsätter
G16: Kodat uppsåt
Kod ska i möjligaste mån beskriva vad det är den gör. Att då skriva saker som m_variabel, eller strNameBtn är inte speciellt mycket till hjälp idag. En gång i tiden var man tvungen att använda förkortningar av olika slag för att man hade ett begränsat antal tecken att använda vid namngivning men idag existerar inte den anledningen. Förr hade man inte en IDE som Netbeans eller Eclipse som kan visa med färger om en variabel är lokal för metoden eller ej (vilket iofs borde vara uppenbart om man inte gjort långa oläsbara metoder). Låt din kod beskriva vad koden försöker att göra. Undvik att använda koden till att gå runt tekniska brister i din utvecklingsmiljö.
G17: Felaktigt placerat ansvar
Den här punkten blir kanske lite flummig men det är nog den som är svårast att skriva något konkret om. Vad är det som gör att man placerar en variabel i den ena klassen eller den andra eller kanske till och med skapar en speciell klass för att hålla variabeln? I första hand handlar det om att placera sig in i läsarens position. Att försöka förstå var någon annan skulle förvänta sig att hitta variabeln i fråga. Det man bör försöka att unvika är att bli "smart" och hitta på smarta sätt. Som sagt.. en något flummig punkt men nog så viktig. Läs boken så förstår ni nog förhoppningvis denna bättre än vad jag har gjort. :-)
G18: Olämpligt användning av static
Man skulle kunna tro att alla metoder som inte använder någon av klassens lokala variabler skulle vara lämpliga att göra static. Då skulle andra klasser kunna utnyttja den existerande funktionalliteten och man skulle kunna öka återmvinningen av kod. Dessvärre är det inte riktigt så enkelt. Underhåll försvåras av att man inte kan ärva från klassen i fråga för att lägga till funktionallitet. Tester blir svåra att skriva om den statiska metoden i sin tur anropar andra statiska metoder. Faktum är att det ska finnas mycket tydliga skäl till när en metod ska vara statisk.
G19: Använd förklarande variabler
Om du ska skriva en metod utför ett flertal operationer är följande kod inte speciellt enkel att förstå.
public int calculateActualSalary(){
return getSalary() + addOverTime() - getTaxes()
}
Hur mycket påverkade addOverTime()? Vad är det för objekt som har en metod som heter withDrawTaxes()? Det bättre sättet att implementa metoden hade varit genom att visa vad som händer med hjälp av förklande variabler.
public int caculateActulSalary{
int salary = getSalary();
int salaryWithOverTime = salary + addOverTime();
int salarayWithOverTimeAndTaxesRemoved = withOverTime - getTaxes();
remove salarayWithOverTimeAndTaxesRemoved;
}
Det blir några rader extra med kod men så mycket lättare att förstå vad det är som faktiskt händer. Ni kan säkert komma upp med bättre exempel där det skulle ha underlättat om man kunnat läsa ett tydligt bra namn på en variabel halvvägs in i beräkningen.
G20: Funktionsnamn ska säga vad de gör
Betänk följande:
Date date = Date.today.add(5);
Vad gör add-metoden ovan? Den lägger till något men vad? 5 sekunder eller 5 år? Låt inte den som ska läsa din kod behöva fundera på vad dina metoder faktiskt gör utan skriv det tydligt i metodnamnet. I exemplet ovan hade det varit bättre att ha kallat metoden för addDays eller addYears eller vad nu metoden ovan faktiskt gör.
G16: Kodat uppsåt
Kod ska i möjligaste mån beskriva vad det är den gör. Att då skriva saker som m_variabel, eller strNameBtn är inte speciellt mycket till hjälp idag. En gång i tiden var man tvungen att använda förkortningar av olika slag för att man hade ett begränsat antal tecken att använda vid namngivning men idag existerar inte den anledningen. Förr hade man inte en IDE som Netbeans eller Eclipse som kan visa med färger om en variabel är lokal för metoden eller ej (vilket iofs borde vara uppenbart om man inte gjort långa oläsbara metoder). Låt din kod beskriva vad koden försöker att göra. Undvik att använda koden till att gå runt tekniska brister i din utvecklingsmiljö.
G17: Felaktigt placerat ansvar
Den här punkten blir kanske lite flummig men det är nog den som är svårast att skriva något konkret om. Vad är det som gör att man placerar en variabel i den ena klassen eller den andra eller kanske till och med skapar en speciell klass för att hålla variabeln? I första hand handlar det om att placera sig in i läsarens position. Att försöka förstå var någon annan skulle förvänta sig att hitta variabeln i fråga. Det man bör försöka att unvika är att bli "smart" och hitta på smarta sätt. Som sagt.. en något flummig punkt men nog så viktig. Läs boken så förstår ni nog förhoppningvis denna bättre än vad jag har gjort. :-)
G18: Olämpligt användning av static
Man skulle kunna tro att alla metoder som inte använder någon av klassens lokala variabler skulle vara lämpliga att göra static. Då skulle andra klasser kunna utnyttja den existerande funktionalliteten och man skulle kunna öka återmvinningen av kod. Dessvärre är det inte riktigt så enkelt. Underhåll försvåras av att man inte kan ärva från klassen i fråga för att lägga till funktionallitet. Tester blir svåra att skriva om den statiska metoden i sin tur anropar andra statiska metoder. Faktum är att det ska finnas mycket tydliga skäl till när en metod ska vara statisk.
G19: Använd förklarande variabler
Om du ska skriva en metod utför ett flertal operationer är följande kod inte speciellt enkel att förstå.
public int calculateActualSalary(){
return getSalary() + addOverTime() - getTaxes()
}
Hur mycket påverkade addOverTime()? Vad är det för objekt som har en metod som heter withDrawTaxes()? Det bättre sättet att implementa metoden hade varit genom att visa vad som händer med hjälp av förklande variabler.
public int caculateActulSalary{
int salary = getSalary();
int salaryWithOverTime = salary + addOverTime();
int salarayWithOverTimeAndTaxesRemoved = withOverTime - getTaxes();
remove salarayWithOverTimeAndTaxesRemoved;
}
Det blir några rader extra med kod men så mycket lättare att förstå vad det är som faktiskt händer. Ni kan säkert komma upp med bättre exempel där det skulle ha underlättat om man kunnat läsa ett tydligt bra namn på en variabel halvvägs in i beräkningen.
G20: Funktionsnamn ska säga vad de gör
Betänk följande:
Date date = Date.today.add(5);
Vad gör add-metoden ovan? Den lägger till något men vad? 5 sekunder eller 5 år? Låt inte den som ska läsa din kod behöva fundera på vad dina metoder faktiskt gör utan skriv det tydligt i metodnamnet. I exemplet ovan hade det varit bättre att ha kallat metoden för addDays eller addYears eller vad nu metoden ovan faktiskt gör.
lördag 7 februari 2009
G11 - G15
Nästan halvvägs på G.....
G11: Inkonsekvens
Att inte överraska de som ska läsa din kod är inte oviktigt någonstans. Därför ska man följa de konventioner som finns (och gudarna ska veta att det är tråkigt att göra ibland). Så trots att denna blogg (eller ännu bättre Bob Martins bok) kan ge idéer om vad som är bra och vad som är mindre bra. Börja inte bara att följa regerna här för det kommer att förvirra de som inte förstår varför man gör på ena eller andra sättet. Det gäller inte bara det jag skrivit här utan rent generellt. Använder ni factories för att skapa alla domänobjekt sluta inte använda dem för att du har ett smarare sätt att göra det på. Det är tyvärr inte alltid så att det är rätt att använda de bästa lösningarna.
G12: Skräp
C5, G9 m fl handlar om skräp. Skräp som är i vägen och hindrar dig från att göra ditt jobb. Skräp som du måste lägga tid på helt i onödan. Så jag upprepar vad jag skrivit tidigare. Bort med skiten. Versionhanteringsystemet ska vara tillräkligt bra för att enkelt låta dig titta tillbaka i tiden om skräpet faktiskt skulle visa sig vara något användbart. Men det är versionshanteringsystemet uppgift och inget som ska ligga som kommentarer, död kod eller vilken annan anledning ni kan komma på för att låta skräpet ligga i koden.
G13: Artificiella kopplingar
Kod som inte har faktiska beroenden på varandra ska inte vara sammankopplade. Det kan finnas många anledningar till att att man kopplar ihop två klasser fast de egentligen inte har har med varandra att göra. En url till en databas kan vara smidigt att återanvända där den ligger. Problemet är att man ger en klass kunskap om en annan klass en inte har något med att göra, förutom den där lilla konstanten. Det skulle vara bättre att upprätta en separat klass med konstanter som båda klasserna klasserna kan använda. Då har man inte skapat en artificiell koppling mellan de två klasserna som inte har med varandra att göra.
G14: Funktionsavundsjuka
Feature envy låter mycket bättre men ska man skriva på svenska så... Metoder i en klass ska i första hand manipulera variabler i den egna klassen. När en metod manipulerar variabler (tex med hjälp av getters och setters) i en annan klass man kan säga att den första klassen önskar att den hade de där bra variablerna som den andra klassen har. Denna regel handlar i första hand om ansvarsfördelning. Varför har en klass just de variabler som den har varför ligger de inte i en annan klass. Det finns naturligvis tillfällen där det är lämpligt att bryta mot denna regel men du bör fundera ett var extra när du ser en klass som verkar avundsjuk på en annan.
G15: Val genom argument
Man kan tycka att det är smidigt med en metod som löser flera uppgifter. Dock metoder inte göra mer än en sak och i stället göra det bra och på ett förståligt sätt. Booleska argument är ofta en tydlig signal på att metoden utför flera uppgifter och borde rent generellt brytas isär till två metoder. Det blir lättare att förstå vad det är som händer när man slipper fundera på vad olika inputargument ska ställa till med. Om man behöver en metod för att välja mellan två olika beräkningar så låter man en metod göra valet och andra metoder får stå för själva beräkningen.
G11: Inkonsekvens
Att inte överraska de som ska läsa din kod är inte oviktigt någonstans. Därför ska man följa de konventioner som finns (och gudarna ska veta att det är tråkigt att göra ibland). Så trots att denna blogg (eller ännu bättre Bob Martins bok) kan ge idéer om vad som är bra och vad som är mindre bra. Börja inte bara att följa regerna här för det kommer att förvirra de som inte förstår varför man gör på ena eller andra sättet. Det gäller inte bara det jag skrivit här utan rent generellt. Använder ni factories för att skapa alla domänobjekt sluta inte använda dem för att du har ett smarare sätt att göra det på. Det är tyvärr inte alltid så att det är rätt att använda de bästa lösningarna.
G12: Skräp
C5, G9 m fl handlar om skräp. Skräp som är i vägen och hindrar dig från att göra ditt jobb. Skräp som du måste lägga tid på helt i onödan. Så jag upprepar vad jag skrivit tidigare. Bort med skiten. Versionhanteringsystemet ska vara tillräkligt bra för att enkelt låta dig titta tillbaka i tiden om skräpet faktiskt skulle visa sig vara något användbart. Men det är versionshanteringsystemet uppgift och inget som ska ligga som kommentarer, död kod eller vilken annan anledning ni kan komma på för att låta skräpet ligga i koden.
G13: Artificiella kopplingar
Kod som inte har faktiska beroenden på varandra ska inte vara sammankopplade. Det kan finnas många anledningar till att att man kopplar ihop två klasser fast de egentligen inte har har med varandra att göra. En url till en databas kan vara smidigt att återanvända där den ligger. Problemet är att man ger en klass kunskap om en annan klass en inte har något med att göra, förutom den där lilla konstanten. Det skulle vara bättre att upprätta en separat klass med konstanter som båda klasserna klasserna kan använda. Då har man inte skapat en artificiell koppling mellan de två klasserna som inte har med varandra att göra.
G14: Funktionsavundsjuka
Feature envy låter mycket bättre men ska man skriva på svenska så... Metoder i en klass ska i första hand manipulera variabler i den egna klassen. När en metod manipulerar variabler (tex med hjälp av getters och setters) i en annan klass man kan säga att den första klassen önskar att den hade de där bra variablerna som den andra klassen har. Denna regel handlar i första hand om ansvarsfördelning. Varför har en klass just de variabler som den har varför ligger de inte i en annan klass. Det finns naturligvis tillfällen där det är lämpligt att bryta mot denna regel men du bör fundera ett var extra när du ser en klass som verkar avundsjuk på en annan.
G15: Val genom argument
Man kan tycka att det är smidigt med en metod som löser flera uppgifter. Dock metoder inte göra mer än en sak och i stället göra det bra och på ett förståligt sätt. Booleska argument är ofta en tydlig signal på att metoden utför flera uppgifter och borde rent generellt brytas isär till två metoder. Det blir lättare att förstå vad det är som händer när man slipper fundera på vad olika inputargument ska ställa till med. Om man behöver en metod för att välja mellan två olika beräkningar så låter man en metod göra valet och andra metoder får stå för själva beräkningen.
G6 - G10
Om man nu trodde att det skulle vara nära slutet nu så har man fel... G6 till G10
G6: Kod i fel abstraktions nivå
Vi människor är duktiga på att blanda abstraktionsnivåer. Ta tex när vi kör bil så kan de flesta klara av uppgiften att styra bilen med gaspedal, broms och ratt. Dessutom gör vi en uppgift som inte har direkt med att framföra bilen. Vi reglerar varvtalet på motorn med hjälp av växelspaken. Tänk efter nu. Många bilar har automatlåda så varför har vi en växellåda. Att behöva lyssna efter vilket varv motorn har måste rimligtvis vara en distraktion från de uppgifter som rimligtvis måste vara viktigare, som att hålla bilen på vägen. Samma regel gäller när vi skriver klasser och API:er. Vi ska inte förvirra användarna genom att ge dem möjligheter att utföra perifiera uppgifter. Med metoder så ska vi bryta ut privata metoder tex för att hantera loopar och kompilicerade if.
G7: Klasser beroende av sina arvingar
Denna regel får inte följas för bokstavstroget. Det finns undantag som vid användandet av Template-method där man faktiskt vill att subklassen ska anropas från basklassen. Men har man inte en riktigt bra motivation som i Template-method-fallet så är det dålig karma att bas-klasserna har beroenden i sina subklasser. Beroendet mellan bas och subklass bör vara enkelriktat.
G8: För mycket information
Det är lätt att låta en klass göra allt för att den redan finns och det inte är något extra arbete med att fundera vad man faktiskt vill att klassen ska utföra och ännu viktigare än vad den inte ska utföra. Den generella regel är att desto mindre en klass visar i sitt publika api desto bättre. Desto färre metoder en klass har desto bättre. Desto färre variabler en klass har desto bättre. Kan man hålla klasserna små tenderar de till ha färre beroenden och där med vara lättare att förändra. Splitta klasser som är stora och svåra att hantera till något som är lätt att hantera.
G9: Död kod
Precis som med C5. Det som inte används ska inte finnas i den aktiva kodbasen. Punkt slut. If-satser som inte kan inträffa. Metoder som inte anropas. Allt ska bort. Det är distraktionen och programmering är tillräkligt svårt utan att bli distraherad av skräp.
G10: Vertikalt avstånd
Denna handlar om hur man ska placera kod i förhållande till annan kod. Grundregeln är enkel. Allt ska vara så nära som möjligt från där det används. Jag upprepar det som står i boken mer eller mindre rakt upp och ner:
G6: Kod i fel abstraktions nivå
Vi människor är duktiga på att blanda abstraktionsnivåer. Ta tex när vi kör bil så kan de flesta klara av uppgiften att styra bilen med gaspedal, broms och ratt. Dessutom gör vi en uppgift som inte har direkt med att framföra bilen. Vi reglerar varvtalet på motorn med hjälp av växelspaken. Tänk efter nu. Många bilar har automatlåda så varför har vi en växellåda. Att behöva lyssna efter vilket varv motorn har måste rimligtvis vara en distraktion från de uppgifter som rimligtvis måste vara viktigare, som att hålla bilen på vägen. Samma regel gäller när vi skriver klasser och API:er. Vi ska inte förvirra användarna genom att ge dem möjligheter att utföra perifiera uppgifter. Med metoder så ska vi bryta ut privata metoder tex för att hantera loopar och kompilicerade if.
G7: Klasser beroende av sina arvingar
Denna regel får inte följas för bokstavstroget. Det finns undantag som vid användandet av Template-method där man faktiskt vill att subklassen ska anropas från basklassen. Men har man inte en riktigt bra motivation som i Template-method-fallet så är det dålig karma att bas-klasserna har beroenden i sina subklasser. Beroendet mellan bas och subklass bör vara enkelriktat.
G8: För mycket information
Det är lätt att låta en klass göra allt för att den redan finns och det inte är något extra arbete med att fundera vad man faktiskt vill att klassen ska utföra och ännu viktigare än vad den inte ska utföra. Den generella regel är att desto mindre en klass visar i sitt publika api desto bättre. Desto färre metoder en klass har desto bättre. Desto färre variabler en klass har desto bättre. Kan man hålla klasserna små tenderar de till ha färre beroenden och där med vara lättare att förändra. Splitta klasser som är stora och svåra att hantera till något som är lätt att hantera.
G9: Död kod
Precis som med C5. Det som inte används ska inte finnas i den aktiva kodbasen. Punkt slut. If-satser som inte kan inträffa. Metoder som inte anropas. Allt ska bort. Det är distraktionen och programmering är tillräkligt svårt utan att bli distraherad av skräp.
G10: Vertikalt avstånd
Denna handlar om hur man ska placera kod i förhållande till annan kod. Grundregeln är enkel. Allt ska vara så nära som möjligt från där det används. Jag upprepar det som står i boken mer eller mindre rakt upp och ner:
- Lokala variabler ska deklareras raden ovan första användning
- Privata metoder ska deklareras direkt efter första användning. Målet är att när man läser en metod ska man ha relaterad kod så nära som möjligt. Detta är inte helt enkelt när den privata metoden används av flera andra metoder men man får göra sitt bästa. :-)
fredag 6 februari 2009
G1 - G5
Då fortsätter vi ... många små regler kvar... men nu är det G1 till G5 som gäller.
G1: Flera språk i samma fil
Det finns en anledning till att framework som Wicket och andra jobbar hårt på att inte blanda html och java utan att de som är bra på respektive sak kan jobba med sin sak utan att bli störda av sådant som för dem är irrelavant.
G2: Förväntat beetende är inte implementerat
Tänk er följande metodsignatur:
public int dayToString(String day)
Denna metod översätter texten "monday" till 1. Fundera nu på vilka textsträngar ni skulle förvänta er att metoden kan tolka. Visst är det så att vi förväntar oss att metoden ska kunna tolka resten av veckodagarna? Är det så att vi förväntar oss att en sådan metod ska kunna olika case som MONDAY och monday eller kanske till och med förkortningar? Vad mer borde vi kunna förvänta oss av metoden? Allt detta ni kan förvänta er borde ni också implementera. Om den som ska använda metoden inte får det resultat den förväntar sig (inom rimliga gränser naturligtvis) så kommer personen ifråga att börja fundera på om koden faktisk gör som han/hon förväntar sig i något fall.
G3: Felaktigt beteende vid gränser (boundaries, cornercases)
Det låter självklart så att koden ska göra rätt i alla sammanhang. Men det är också väldigt enkelt att glömma bort att som programmerare glömma bort, eller vid stress "glömma bort" att faktiskt lägga tid på att försäkra sig om att koden faktiskt gör rätt om något annat data än det normala. Även om metoden gör rätt i 10 000 fall så är inte koden rätt förrän alla möjliga utfall är korrekta.
G4: Borttagna säkerhets funktioner
Säkerhet är nästan alltid i vägen så är det. Men oftast finns det en anledning till att de finns där i första rummet. Att strunta i dem är i bästa fall dumdristigt. Glöm inte att säkerhet är mycket mer än bara inloggningar. Kompileringsvarningar tex upplyser om att du kan ha ett problem snart. Varningar om att ett program inte hittade sin konfigurationsfil och därför föll tillbaka på defaultvärden innebär att när det väl kommer en konfigurationsfil kan det få stora effekter på ditt program.
G5: Duplicering
Don't repet yourself (dry), Once and only once. Kärt barn har många namn. Allt går ut på att om man har kod som gör samma sak på två ställen så är det mer än dubbelt så svårt att underhålla koden och det blir lätt att få sin applikation att uppföra sig okonsekvent där vissa funktioner ibland fungerar och ibland inte fungerar berorende att vissa kopior har ändrats och vissa inte. Att vara nogrann med att söka efter existerande funktionallitet räcker långt men om du ska vara seriös skaffar du ett verktyg (inställt på högsta möjliga känslighet) och börjar åtgärda de värsta problemen och forsätter tills hela kodbasen är fri från dupliering. Ibland måste man skriva om koden till att tex använda Template method eller Strategy pattern för de fall där en metod återkommer i koden med små förändringar. Ibland måste man fundera på om man kanske ska byta ut if/else och switch mot polymorfism (don't ask, tell)
G1: Flera språk i samma fil
Det finns en anledning till att framework som Wicket och andra jobbar hårt på att inte blanda html och java utan att de som är bra på respektive sak kan jobba med sin sak utan att bli störda av sådant som för dem är irrelavant.
G2: Förväntat beetende är inte implementerat
Tänk er följande metodsignatur:
public int dayToString(String day)
Denna metod översätter texten "monday" till 1. Fundera nu på vilka textsträngar ni skulle förvänta er att metoden kan tolka. Visst är det så att vi förväntar oss att metoden ska kunna tolka resten av veckodagarna? Är det så att vi förväntar oss att en sådan metod ska kunna olika case som MONDAY och monday eller kanske till och med förkortningar? Vad mer borde vi kunna förvänta oss av metoden? Allt detta ni kan förvänta er borde ni också implementera. Om den som ska använda metoden inte får det resultat den förväntar sig (inom rimliga gränser naturligtvis) så kommer personen ifråga att börja fundera på om koden faktisk gör som han/hon förväntar sig i något fall.
G3: Felaktigt beteende vid gränser (boundaries, cornercases)
Det låter självklart så att koden ska göra rätt i alla sammanhang. Men det är också väldigt enkelt att glömma bort att som programmerare glömma bort, eller vid stress "glömma bort" att faktiskt lägga tid på att försäkra sig om att koden faktiskt gör rätt om något annat data än det normala. Även om metoden gör rätt i 10 000 fall så är inte koden rätt förrän alla möjliga utfall är korrekta.
G4: Borttagna säkerhets funktioner
Säkerhet är nästan alltid i vägen så är det. Men oftast finns det en anledning till att de finns där i första rummet. Att strunta i dem är i bästa fall dumdristigt. Glöm inte att säkerhet är mycket mer än bara inloggningar. Kompileringsvarningar tex upplyser om att du kan ha ett problem snart. Varningar om att ett program inte hittade sin konfigurationsfil och därför föll tillbaka på defaultvärden innebär att när det väl kommer en konfigurationsfil kan det få stora effekter på ditt program.
G5: Duplicering
Don't repet yourself (dry), Once and only once. Kärt barn har många namn. Allt går ut på att om man har kod som gör samma sak på två ställen så är det mer än dubbelt så svårt att underhålla koden och det blir lätt att få sin applikation att uppföra sig okonsekvent där vissa funktioner ibland fungerar och ibland inte fungerar berorende att vissa kopior har ändrats och vissa inte. Att vara nogrann med att söka efter existerande funktionallitet räcker långt men om du ska vara seriös skaffar du ett verktyg (inställt på högsta möjliga känslighet) och börjar åtgärda de värsta problemen och forsätter tills hela kodbasen är fri från dupliering. Ibland måste man skriva om koden till att tex använda Template method eller Strategy pattern för de fall där en metod återkommer i koden med små förändringar. Ibland måste man fundera på om man kanske ska byta ut if/else och switch mot polymorfism (don't ask, tell)
Prenumerera på:
Inlägg (Atom)