fredag 11 februari 2011

S - och vad det innebär för oss dödliga utvecklare

Efter att ha hört Hadi Hariri prata om maintainable code på ett event i Göteborg häromdagen fick jag en sista knuff att ta tag i något jag funderat över ett tag, nämligen SOLID.

SOLID är en akronym av akronymer som myntades av Robert C. Martin (Uncle Bob), en levande legend i utvecklarsamanhang och författaren till den fantastiska boken Clean Code. SOLID står för fem principer:
  • Single Responsibility
  • Open/Closed
  • Liskov substitution
  • Interface segregation
  • Dependency Inversion
Dessa begrepp har många gånger förklarats och diskuterats. Bland annat finns det en hel del referenser till bra podcasts om dem i ett av mina gamla blogginlägg

Man kan då undra vilket värde det finns i att gå igenom dem igen, men jag tänkte för egen del ta tag i att lära mig dem en gång för alla. Hur kan jag applicera dem i mitt vanliga utvecklarliv, som affärssystemsutvecklare? Eftersom jag är ett unit-testing freak vill jag också gräva lite i hur väl de sammanfaller med testbarhet.

Därför börjar vi idag med S - Single Responsibility Principle (SRP) som definieras såhär

There should never be more than one reason for a class to change
             (http://www.objectmentor.com/resources/articles/srp.pdf)


En klass ska alltså ha ett enda skäl att ändras. Det klassiska exemplet är en klass "Anställd", som ansvarar för att spara och hämta sig från databasen, räkna ut sin lön och skriva ut en rapport. I ett sånt tillrättalagt exempel är det ju enkelt att se att den har många orsaker att ändra sig. Databasens struktur innebär ändringar. Löneuträkningen innebär ändringar. Rapportformatet innebär ändringar.

Jag tror inte att någon av oss skriver sådana klasser... eller? Ett mer vanligt fenomen är att vi skriver "god-classes", klasser som kan det mesta och lite till. Ett säkert tecken är namnet. När vi känner oss tvungna att lägga på ändelser som "Service", "Manager" eller "Handler" på vår klass ska vi börja fundera. Vad gör vår klass, egentligen?

En annan vanlig sak är att vi smyger in infrastruktur lite här och var, som säkerhet och loggning. Många klasser i systemet  loggar via en logger och varje klass kollar att användaren är auktoriserad. Ändrar vi på någon av dessa tekniker åker vi dit igen. Här kan AOP vara en lösning, men det är en helt annan diskussion.

Ett enklare sätt att titta på SRP är att börja på metodnivå och sedan arbeta sig uppåt. SRP gäller nämligen även där! Här är det ännu enklare att detektera "smells", tecken på att vårat S är i fara:

  • Långa metoder
    • En metod på 300 rader gör troligen mer än en sak. En metod på 10 rader gör troligen färre saker...
  • Otydliga metodnamn
    • DoWork() - gör säkert bra saker, men vad? 
    • UpdateOrInsert() - gör ju åtminstone två saker
    • CreateAndRegisterItem() - dito
    • CreateItemIfTotalIsNotTooHigh() - ja, kommentar kanske är överflödig...
    • Generells sett, undvik if, and och or i metodnamn...
  • Metoder med bool parametrar för att styra flödet
    • PrintReport(bool toPrinter) kan ju tydligen skriva ut både till printer och någon annanstans
    • PrintReport(bool toPrinter, bool includeDeleted) - mera bools, mera permutationer....
  • Metoder som arbetar på flera abstraktionsnivåer
    • En metod som läser fil från disk, parsar innehållet och agerar därefter utifrån affärslogik arbetar på flera abstraktionsnivåer och gör definitivt många saker...

Det enklaste sättet att kolla sig själv är att alltid försöka beskriva exakt vad metoden gör med enbart namnet. Kan man inte det med enkelhet så luktar det lång väg...

När man väl förstår metodnivån kan man dra det längre och applicera det på klassnivå. En Parser klass har ett tydligare definierat ansvar än en FileHandler. Så att försöka namnge saker exakt efter vad de gör är gångbart även på klassnivå.

Ett annat tips som Hadi Hariri tog upp i sin session i Göteborg och i sitt eminenta blogginlägg är att byta ut parametrarna i sina metoder mot instansvariabler. Kolla sedan hur många metoder i klassen som använder dessa instansvariabler. Är det få har vi låg cohesion i klassen, dvs metoderna hör inte ihop. Arbetar de inte på någon instansvariabel kommer Resharper att vilja göra metoden static. Både cohesion och static metoder kan vara tecken på att man bör bryta isär till fler klasser.

Det här innebär givetvis att vi kommer få många små klasser med många små metoder. Ett argument mot det kan ju vara att man inte vill hålla reda på så många klasser i sitt projekt. Men väg det mot att få i uppdrag att lösa en bugg i en klass på 1500 rader, eller en metod på 300 rader... Och det där med att hålla reda på löser de flesta utvecklingsmiljöer rätt bra, dessutom med verktyg som Resharper är varje klass bara ett CTRL+T bort... Så glöm det!

Hur påverkar SRP testbarhet?
  • Att skriva test för en metod med tydlig namn och få rader kod är enkelt. 
  • Att skriva test för en klass med begränsat ansvar innebär troligen färre beroenden och kollaboratörer. Därmed färre saker att isolera i en testmiljö.
  • Små klasser = små testklasser = samma nytta för underhåll som för produktionskod.
Sammanfattning
  • Håll koll på namn på metoder och klasser. Är det svårt att komma på = varningslampa!
  • Håll metoder och klasser korta
  • Granska klassens cohesion
  • SRP underlättar testbarhet
Det verkar som om jag kan skriva under på S:et i SOLID. Det är en av de enklare principerna att förstå men kan vara lurigt att implementera. Använd några av teknikerna med namngivning eller instansvariabler till hjälp. Ibland går det inte, men jag tycker att vår ambition är det viktigaste. Att vi förstår att SRP är ett perspektiv att granska sina klasser och metoder utifrån.

Sådär ja! One down, four to go. Next stop: Open/Closed.

Over and out!
[Dagens kodarmusik: Jochen Miller - Brace yourself]

1 kommentar:

  1. Sådär ja! One down, four to go. Next stop: Open/Closed. 2011.. när kommer nästa? :D

    SvaraRadera