SOLID programmering; Del 1

2012-10-22

SOLID är en akronym för 5 principer inom programmering som om de används korrekt hjälper dig att skapa stabil, utvecklingsbar och kvalitativt bra kod. SOLID introducerades av Michel Feathers och har uppmärksammats och lyfts fram av Robert C. Martin. SOLID skall ses mer som riktlinjer än som regler då det alltid kommer att finnas tillfällen då man inte kan tillämpa en eller flera av dessa principer, men om man alltid strävar efter att följa dem så kommer man i längden att underlätta livet för både dig själv och andra programmerare.

Jag tänkte ta upp dessa fem principer i en serie på 5 blogginlägg, och titta lite närmare på vad de står för. Jag börjar från början med bokstaven ”S” – ”Single Responsibility Principle”.

Del 1 – Single Responsibility Principle

Single Responsibility Principle deklarerar att varje klass skall ha endast ett ansvar och det ansvaret skall vara till fullo inkapslad i klassen. Alla de tjänster som klassen tillhandahåller skall vara för att tillgodose det ansvaret och ingenting annat.

Vad innebär detta? Vad är ”endast ett ansvar”? Såsom jag ser det så är definitionen av ”endast ett ansvar” beroende av vad klassen förväntas utföra. T.ex. en klass skall endast hämta en kund från kundregistret. Den skall inte både göra det OCH skriva ut kunduppgifterna på webben. Detta är två skilda ansvar. Det gör att om man behöver ändra i kundregistret så påverkas bara klassen som hämtar kunduppgifterna och ingenting i front-end påverkas. Och det är ju precis det vi vill – kunna ändra utan att påverka andra saker.

Robert C. Martin definierar detta ansvar till att vara att

en klass skall endast ha en anledning att förändras.

Han jämför med att en klass inte skall sammanställa/kompilera (compile) och skriva ut en rapport därför att den då har två anledningar till att förändras: Antingen kan rapporten (data) förändras eller så kan formatet för rapporten ändras. Detta gör att det blir svårt att ändra i klassen.

Jag skulle vilja ta denna princip ytterligare ett steg ner i klass-hierarkin: Även metoderna i en klass skall utgå från den här principen: De skall hantera/utföra endast en sak, alternativt ha endast ett ansvarsområde (enligt Robert C Martins definition). Så fort de har ansvar för flera saker så skall den delas upp i flera metoder. Detta gör att metoden då blir lättare att testa med Unit Tester och färre fel letar sig in i koden.

Ett sätt att hitta en klass eller metod som har för stort ansvarsområde är att om du har svårt att ge klassen/metoden ett meningsfullt namn som tydligt specificerar vad den gör – då har du troligtvis fel scope på din klass/metod och bör bryta upp den i två eller flera block. I mitt exempel så blir det tydligt: Jag kan döpa min klass till t.ex. ”CustomerLookup”; det är tydligt att då avses endast en läsning från kundregistret på en (1) kund. Inget annat. Om jag hade varit tvungen att döpa den till ”CustomerLookupAndPrint” så avslöjas att klassen gör för mycket. Ordet ”And” betyder ju att det är två saker som kombineras…

Glöm inte del 2 i Single Responsibility Principle!

Glöm nu inte bort att även de exponderade metoderna skall stödja ansvaret och ingenting annat! Det är väldigt lockande att ge en massa publika metoder bara för att underlätta saker och ting, men som i själva verket inte är klassens ansvar.

Ta t.ex. min klass ”CustomerLookup” som har som ansvar att hämta en kund från kundregistret. Det är väldigt lockande att expondera toString() och därigenom bestämma hur den skall formatteras på webben. Faktum är att detta är ju inte klassens ansvar! Således skall toString() inte bestämma över formattering. Däremot skall CustomerLookup expondera en metod för att ge t.ex. kundens namn ”getCustomerName()” (eller bara customerName()), och en annan metod för adressen osv. Då får klassen som sedan hanterar formattering på webben själv bestämma vilken uppgift som placeras vart och hur.

Som kuriosa; Vad skall då toString() vara i CustomerLookup? Personligen skulle jag använda toString() för att ge hela kund-recordsetet i en kommaseparerad sträng. Då är det lätt att använda den i en jämförelse för att se om kunduppgifterna har ändrats;

CustomerLookup customer = new CustomerLookup("777777-7777");
String compare = customer.toString();
String anotherCompare = "7777777-7777,Nisse Hult, Avägen 1, 999 99 Storstad";
if (compare.equalsIgnoreCase(anotherCompare)) {
//Nothing has changed...
} else {
//Oh.... Something is different here!
}

Ok, nu hamnade vi på ett litet sidospår här… men jag tror jag har gjort tydligt om vad Single Responsibility Principle innebär för mig.

Detta är för mig en av de viktigaste principerna i hela SOLID-akronymen. Det räcker väldigt långt att börja med denna princip. Följ mig i nästa avsnitt så skall jag gå igenom nästa bokstav: ”O” – ”Open/Closed principle”!

Läs mer…

SOLID SOLID (Object-oriented Design) enligt Wikipedia.
Single Responsibility Principle SRP – Single Responsibility Principle enligt Wikipedia.

Annonser

Kommentera inte din kod!

2012-10-02

Det här är ett ämne som delar oss programmerare i två väsenskilda läger; De som anser att man SKA kommentera i kod och de som anser att kommentarer är överflödiga.

Är du en duktig programmerare så kommer kommentarer att vara onödiga; du kan strukturera koden så att den blir lättläst och lätt att förstå. En dålig progammerare behöver kommentarer för att fylla upp sina kunskapsluckor, vilket egentligen bara gör att koden blir ännu mer svårläst. Dålig kod blir tyvärr inte bättre med kommentarer – snarare tvärtom. Ännu mer text att läsa för att förstå koden… Inte precis rätt väg att gå om man tänker lite logiskt på problemet. Bättre då att strukturera sin kod att den går att läsa och förstå lika lätt som en bok eller en tidning.

Vilka är då de vanligaste felen? Ja, i den kod som jag sett genom åren så är det följande:

Kommentaren kommenterar kod som är självklar

T.ex.

private double summery() {
double first = 1;
double second = 1;
//Returnera summan av first och second:
return first + second;
}

Vad i hela friden gör kommentaren för nytta där? Den gör bara att mängden text som ögat måste scanna igenom blir större och det tar längre tid för dig att läsa och förstå koden i sin helhet. Bort med sådana kommentarer!
Jag kan förstå att man lägger in sådana här kommentarer när man ägnar sig åt pseudo-kodning, men när man är klar med den riktiga koden så skall sådana kommentarer raderas!

Kommentar är helt felaktig


private double summary() {
double first = 1;
double second = 1;
//Returnera summan av first och second:
return first * second;
}

Här kommer man att hoppa till när man läser: Vad är det som är rätt? Är det kommentaren som är korrekt och koden felaktig eller tvärtom? Detta härrör sig troligtvis från att kommentaren en gång i tiden har varit helt korrekt, men sedan har förutsättningarna ändrats, koden har justerats, men kommentaren ligger kvar orörd.
Ta bort sådana här kommentarer i sin helhet! De kommer aldrig att uppdateras och skapar bara förvirring om vad som faktiskt gäller när man skall korrigera buggar, korrigera ändrade förutsättningar mm.

Det här felet är mycket vanligt i Java program och framförallt när programmeraren har använt editorns inbyggda funktion för att automatiskt lägga till en JavaDoc-stub. JavaDoc kommentarer har en förmåga att snabbt bli föråldrade och felaktiga. Ju mer programmeraren har lagt in i JavaDoc-kommentaren, desto större är risken att den innehåller felaktigheter. Så vilken nytta fyller då JavaDoc-kommentaren?

Kommentar ligger på fel plats

Inte så vanligt fel, men det inträffar. Man kan efter att ha kollat igenom hela koden kommit på att en viss kommentar faktiskt är felplacerad och borde ligga i en annan funktion eller på annan plats i koden. Detta inträffar då man har gjort ”klipp-och-klistra” kodning: Man har flyttat koden som kommentaren tillhör men inte själva kommentaren. Den ligger kvar på ursprunglig plats och detta skapar förvirring (återigen).

Men behövs aldrig kommentarer då?

Jo, det finns alltid undantag till alla regler. Det kan ibland hända att koden blir välidgt komplex och man behöver förklara vilken affärslogik som ligger bakom koden, eller helt enkelt en kommentar att ”det borde finnas ett bättre sätt att göra detta, men jag vet inte”. Dvs det kan finnas tillfällen då du känner på dig att koden är onödigt komplex, men du vet inte hur man skall kunna göra detta på bättre sätt. Då kan kommentaren fungera som en ”TODO”-minnesanteckning tills att du har hunnit fråga en kollega eller kollat upp på nätet. Men detta innebär att du har som mål att även eliminera denna kommentar!

Men det är väl bra att skriva JavaDoc så att användaren av min funktion vet vilka parametrar som förväntas och vad som kommer ut av funktionen?

Nej. En funktion med ett väldigt tydligt funktionsnamn som beskriver vad den gör, kommer att bättre tala om för mig vad det blir för resultat av funktion. De parametrar som behövs skall vara tydligt namngivna så att jag inte behöver fundera på vilken betydelse de har. Då behövs ingen JavaDoc. Om funktionen är så komplex att funktionen måste beskrivas, då behöver du troligtvis skriva om hela funktionens arkitektur. Något står inte rätt till då. Dessutom har jag sett allt för många exempel på JavaDoc som inte blivit uppdaterad då funktionen/klassen har blivit uppdaterad. Detta skapar förvirring. Funktionen/klassen kommer att utföra något på ett annat sätt än vad som är beskrivet i kommentaren. Vad är det som gäller? Resultatet eller kommentaren?

Ett litet test: Vilken av följande går snabbast att läsa och förstå?


/*
* This function will search all books
* in the database and return the author
* of a book with provided ISBN
*
* Parameter: searchISBN (String)
* Return: Name of Author (String)
*/
function String search(searchISBN) {
....
}

eller den här:


function String getAuthorOfBook(String ISBN) {
...
}