måndag 7 mars 2011

Badare, skogshuggare och objektorienterade designers

Ibland kastas man tillbaka till skolbänken. Inte i bokstavlig mening, som i att gå en kurs, utan bildligen. Ibland blir man påmind om vad man faktiskt lärde sig där bland alla föreläsningar, fikapauser och tentor. Man blir påmind om hur lite man egentligen vet och hur spännande vårt område egentligen är.

Sitter fortfarande med samma avancerade problem som jag bloggade om förra gången. En avancerad urvalsprocess för att välja det bästa alternativet bland många. Jag vet nu vet vad som ska göras och hur det bör gå till men jag hade stora problem med att få lösningen dit jag ville. Jag ritade pilar och boxar, hittade på coola namn som "Manager", "RuleSet" och "Matcher". Problemet jag hade var att det inte kändes objektorienterat. På något vis luktade hela lösningen procedurell kod... Dessutom blev de tester jag skrev krångliga och allt för nyfikna.

Låt oss ta en avstickare och fundera lite över nyfikna tester. De är ett problem. Det är tester som vill veta lite för mycket om klassen som det testar. Istället för att testa inputs och outputs testar de istället hur klassen under test kommer fram till ett resultat. Tänk dig en metod som processar en fil. Den ska givet ett antal villkor ta olika beslut och då och då interagera med andra klasser. Det vi egentligen vill veta är att vår metod uppfyller sitt kontrakt, att den gör det den utger sig att göra. Men ett nyfiket test kollar istället på vilket sätt den interagerar med omvärlden och "tjuvlyssnar" på algoritmens inre logik. Visst, du kanske har snott ihop världens tajtaste algoritm, men jag kan lova dig att det finns någon där ute som kan göra det annorlunda och troligen bättre. Om denne någon fick i uppdrag att modifiera din algortim skulle de nyfikna testerna stå ivägen och bara orsaka svordomar. De skulle faila bara för att man ändrade på algoritmens struktur, fastän det förväntade beteendet var oförändrat. Varje gång vi använder mocks och inte stubs bör vi därför ställa oss frågan: "är jag för nyfiken".

Ok, det var ett sidospår. Jag satt alltså där med en lösning på gång, men med en lukt av gammal unken Pascal.

Återigen ett samtal med en kollega. Återigen en ny syn på saken, men denna gång en sanning jag har kunnat men glömt bort. Frågan han ställde var enkel och förtjänar fet stil i stor font


"Hur skulle det du försöker göra fungera om det gjordes manuellt, i verkligheten?".

En alldeles lysande fråga! Jag tänkte bort alla artificiella saker jag uppfunnit där på min kammare. Jag började förklara flödet och beskrev deltagarna i mitt scenario som personer. Dessa personer hade inga collections eller managers till sin hjälp. De hade böcker, offerter, anteckningar och andra verkliga saker. De interagerade inte med repositories eller factories. De interagerade med andra personer. En skiss dök upp. Pilar fortfarande, men nu streckgubbar och dokument. Regelböcker och arkivmappar.



Det fantastiska med det här var att jag genast fick en objektorienterad lösning. En lösning som modellerade något verkligt och som efterliknade verklig kommunikation och interaktion. Inga Managers, Coordinators eller Services. Helt plötsligt föll sig också kollaboratörernas respektive ansvarsområden och förmågor på plats.

Jag hade helt glömt att objektorientering mår bäst när den modellerar något verkligt. Den mår bäst av att vi inte hittar på massa abstraktioner och flummiga tekniska begrepp. Den mår bäst när vi använder vårt eget språk som vi skulle använda för att beskriva för vem som helst. En snabb tur till Google ger snart denna mening som förtjänar att dammas av: "Software objects are often used to model the real-world objects that you find in everyday life."[källa] Det tål att tänkas på när vi springer iväg med vårt design-pattern-influerade, tekniktörstande ego...

 Vad gäller nyfikna tester slapp jag även dem. Eftersom varje klass nu fick ett tydligt ansvar och tydliga metoder blev det enkelt att testa. Om varje klass får ett tydligt kontrakt är det enkelt att sätta förväntningar och se att de infrias av klassen. Jag tror att nyfikna tester är en indikation på att man pysslar med procedurell kod. Det funkar inget vidare. Tydligen gillar TDD objektorienterad kod bättre än Pascal.

Och så till rubriken. Vad har någon som badar eller kör motorsåg gemensamt med den som sysslar med objektorienterad design? Tja, jag gillar ju både bad och motorsågar (kanske inte i kombination...) men jag är inte den gemensamma nämnaren.

Nä, svaret är enkelt: de är alla saker vi inte bör göra ensamma.

Over and out!
[Dagens kodarmusik: Blow - Ke$ha]

fredag 25 februari 2011

Ett par nya ögon och öron kan spela stor roll!

Satt häromdagen och slet mitt hår över en riktigt komplex uppgift. Jag ritade klassdiagram, körde lite TDD, skapade en massa interface.... bara för att inse att jag var ute och cyklade fullständigt. Den känslan är inte ny. Den är tvärtom välbekant.

Jag tror det finns en motsvarighet till författarnas "writers block" även inom vårt yrke. Den där känslan man får när man provat alla angreppsvinklar man har och ändå slutar upp i återvändsgränder. Det är tröstlöst. Det är svårt skadande för självförtroendet. Så ger man upp, slår igen laptopen och slänger sig i soffan med bultande huvudvärk och en känsla av hopplöshet.

Men det tar inte slut där. Då kommer natten. Man vänder och vrider sig och försöker lösa problemet lite sådär halvsovande. Jag minns när jag gick på högskolan och vaknade i flummiga tankar i stil med "hur jag än vänder och vrider på kudden så löser jag inte den här labben". För någon utanför vårt yrke tror jag det låter hur galet som helst. Men jag tror att vi alla har varit där någon gång...

Men sen kommer ljuset - ett par nya ögon eller öron. Man drar ihop en eller ett par kollegor och presenterar problemet för dem. Kanske löser man det bara genom att förklara för dem, genom att höra sina egna ord. Eller så är det någon som bara ser. Ser det du inte kunde se. Någon som får den där förlösande upptäckten som gör att min egen hjärna får en välbehövlig defibliratorchock. Som en LP som hakat upp sig som någon knuffar vidare. Det är en grym känsla!

Ibland är man själv den nytänkande motparten, ibland är man den som kört fast. Oavsett situation är de nya ögonen ovärdeliga.

Dessutom tror jag att man måste igenom det där jobbiga för att kunna ta emot de nya ögonen. Man måste gräva ner sig i ett problem och vända det ut och in före man verkligen kan uppskatta en ny dimension och förstå att den har någon värdefullt att ge.

Precis det här hände mitt senaste problem. En oerhört kreativ diskussion med två begåvade kollegor löste många knutar och någon bara såg. Fantastiskt.

Så alla ni som är med i kreativa diskussioner, se ert värde! Även om man inte kommer fram till lösningen just där och då kan era frågor och nya vinklar vara värda guld. Våga säga nåt, våga komma med en fråga eller åsikt, det kan vara just den som är starten till nästa möjlighet!

Over and out!

Dagens kodarmusik: Sara Lumholdt - Enemy

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]