fredag 27 februari 2009

10 artiklar som alla borde ha läst (minst två gånger)

Läste på http://blog.objectmentor.com/ ett inlägg av Michael Feathers. Han skriver tydligen på en ny bok och hade konstaterat att han hade tappat fokus på vilken publik han skriver för. Vad kan de som lässer boken. Kort och gott så kom han på sig med att förklara förklaringarna vilket inte var så bra. Så han plockade fram en lista på tio artiklar som i sin tur skulle förklara de begrepp han tänkte använda sig av. Några av dem är ganska gamla så det är ingen revolutionerande kunskap på något sätt. Ibland kan det vara bra att titta på annat än det som är hypat idag. Jag tänkte försöka mig på att läsa dessa tio artiklar och dessutom försöka att skriva ihop en kort sammanfattning här (om jag förstår tillräkligt mycket av dem).

De tio artiklarna är:
  • On the criteria to be used in decomposing systems into modules – David Parnas
  • A Note On Distributed Computing – Jim Waldo, Geoff Wyant, Ann Wollrath, Sam Kendall
  • The Next 700 Programming Languages – P. J. Landin
  • Can Programming Be Liberated from the von Neumann Style? – John Backus
  • Reflections on Trusting Trust – Ken Thompson
  • Lisp: Good News, Bad News, How to Win Big – Richard Gabriel
  • An experimental evaluation of the assumption of independence in multiversion programming – John Knight and Nancy Leveson
  • Arguments and Results – James Noble
  • A Laboratory For Teaching Object-Oriented Thinking – Kent Beck, Ward Cunningham
  • Programming as an Experience: the inspiration for Self – David Ungar, Randall B. Smith
  • söndag 22 februari 2009

    Legacy code

    Enligt wikipedia betyder "Legacy code" följande: source code that relates to a no-longer supported or manufactured operating system or other computer technology. Läser man Michel Feathers artikel "Working effectivly with legacy code" får man följande till livs: The main thing that distinguishes legacy code from non-legacy code is tests, or rather a lack of tests. Michel Feathers hävdar att man inte behöver vänta på att support för koden ska försvinna, man kan skriva legacy code, alltså koden du ser på skärmen kan uppfylla definitionen innan du ens tryckt på sparaknappen eller kompilerat den första gången.

    Gammal kod är ofta svår att arbeta med. Ingen som längre jobbar med koden kommer ihåg varför man gjorde på ena eller andra sättet och ännu värre, det är inte någon som vet vad som är rätt eller fel längre, särskilt med hänsyn taget till eventuella förändrade krav från omvärden på vad koden ska göra. Detta är dock brister man kan se efter någon vecka i projekt som har fått börja från början. "Det är N som har skrivit koden, han vet hur den fungerar" är något
    det man kan höra ganska snart in i ett projekt. Något som kan vara ett första tecken på att man nyproducerar Legacy code.

    Varför är då tester boten mot Legacy code? Tester skapar ett förtroende för att man kommer att få reda på om man förstör existerande funktionallitet och med det försvinner det största problemet med Legacy code, rädlan för att man förstör något som idag fungerar. Så Michel Feathers har säkert funderat på varför man ska vänta på att koden blir gammal och osupportad innan man kallar den för legacy code när det största problemet med legacy code kommer redan vid nyproduktion, rädslan för att ändra det som fungerar.

    Men om man nu sitter med ett projekt där det inte finns några tester och ingen längre kan säga hur de tänkte när de skapade koden. Tanken på att sätta sig och börja dokumentera existernade funktionallitet kanske är så upplyftande men på något sätt måste det göras. Hur ska man annars veta om den förändringen man gör inte påverkar annan funktionallitet på ett oavsiktligt sätt.

    Vi dokumenterar den existerande funktionalliteten genom att skriva tester, att sitta och klicka i applikationen är en syssla som människor är dåligt rustad för . Dessa tester är dock något annorlunda. De är inte till för att verifera att den existerande koden gör rätt utan har som enda syfte att upptäcka om den existerande funktionalliteten förändras. Om en metod för ett givet input returnerar true, så har testet som uppgift att tala om när samma input ger false. Hurvida det är rätt att returnera true för det givna inputet är inte viktigt. Det som är viktigt är att det är som koden levererar idag.

    Att skriva tester för en hel applikation är inte riktigt genomförbart dock. Vi måste försöka att titta på var det är vi ska göra vår förändring och sedan försöka att ringa en "trång punkt" genom vilken funktionalliteten styrs idag och testa utifrån denna trånga punkt. Syftet är inte att ge oss en komplett bild utan något som genom en rimlig arbetsinsats kan ge oss en relativt god chans att bli informerade om att vi har förändratat vi borde hållt tassarna borta från.

    Men nu när vi skrivit de tester som verkar rimliga har vi en uppgift till innan vi faktiskt kan påbörja den uppgift som är vårt faktiskta uppdrag. Vi bör refaktorera den existerande koden. Detta genom att bryta ut se till att variabler och metodnamn är vettigt döpta, bryta ut metoder och sist se om vi kan struktuerar klasserna på ett bättre sätt, tex genom att byta ut if/else mot polymorphism. För varje steg kör vi våra tester så att vi inte oavsiktligt har ändrat något.

    Nu är vi äntligen framme. Vi har betalat en del av den skuld som tidigare utvecklare belastat applikationen med och kan utifrån de tester vi skrivit steg för steg skriva om testerna till att testa den funktionallitet vi önskar att applikationen ska ha och till sist anpassa applikationen så att testerna börjar lysa grönt.

    Lät det krångligt? Att förändra kod till nya krav är inte alltid enkelt. Men man kan göra det på ett sätt som ger dålig nattsömn och kräver magsårs medicin eller så försäkrar man sig genom att installera larm som kan upptäcka felaktigheter.



    torsdag 19 februari 2009

    Design Principles and Design Patterns fortsättning

    Tyckte att det blev lite långrandigt så jag bestämde mig för att dela upp min sammanställning av Bob Martins - "Design principles and Design Patterns".

    Nu blir det hur man kan hantera beroenden moduler i en applikation.
    Packet Cohesion Priniciples
    Tre sätt att resonera runt hur man ska förpacka sina moduler
    • The Release Reuse Equivalency Priciple (REP) - REP kopplar ihop releasehantering med återanvändning. De klasser som behövs för att kunna skapa en release av en modul ska grupperas tillsammans och därmed återanvändas tillsammans. Det gör det smidigt tex för en kund att veta att det har hänt tillräkligt med koden för att det ska vara idé att uppdatera även om han/hon bara är intresserad av en liten delmängd av de funktioner som kommer med releasen.
    • The Common Closure Principle (CCP) - Klasser som ändras tillsammans bör förpackas tillsammans. Om man befinner sig i utvecklingsfasen så vill man inte hantera många olika moduler och hålla koll på vilka andra moduler som man är beroende och hur vida de modulerna ändrats på ett sätt som kräver ändring i den egna modulen. Därför minimerar man antalet moduler som behöver uppdateras för en releasecykel.
    • The Common Reuse Principle (CRP) - Klasser som inte används tillsammans ska inte förpackas tillsammans. Varför ska man behöva uppdradera ett operativsystem bara för att få tillgång till en funktion? Förpacka därför bara det som används tillsammans för att inte tvinga på användaren en större förändring än nödvändigt.
    De tre principerna ovan utesluter varandra. REP och CRP gör livet lite lättare för de som vill återanvända kod. CCP tenderar till att ge stora moduler medans CRP ger små. Som tur är vilken princip man väljer att förpacka modulerna inte skrivna i sten. Medans man utvecklar kanske man väljer att använda CCP och sedan när modulerna kanske man går mot något som är lättare för användarna av modulerna att använda som REP eller CRP.

    The Packet Coupling Principles
    De tre kommande principerna styr hur relationerna mellan olika modulerna ska fungera.
    • The Acyclic Dependencies Principle (ADP) - Ganska enkel och självklar. Modulerna ska inte ha cykliska relationer, tex Modul A -> Modul B -> Modul C -> Modul A. Tänk dig att ha dataproviders som är beroende på GUI. Ha någon som håller koll på att cykliska relationer inte smyger sig in i applikationen.
    • The Stable Dependencies Principle (SDP) - SDP säger att man ska försöka att rikta sina beroenden mot det som är stabilt. Vad är då stabilt? Det som är svårt att förändra av olika skäl kan sägas vara stabilt. Den viktigaste faktorn för att en modul är stabil är att den har många andra moduler som är beroende på den. Ska man ändra i modulen så får man ändra på många andra ställen. Herr Martin ställer upp en enkel formel för att beräkna om en modul är stabil eller ej.
      Ca = Inkommande beroenden (klasser utanför modulen som är beroende på modulen i fråga)
      Ce = Utgående beroenden (klasser som är beroende på andra klasser utanför modulen)
      I = Instabilitet - Beräknas I = Ce / (Ca + Ce)
      Så med formeln ovan kan vi säga att SDP betyder: Din modul ska ha högre beroenden än de den har sina beroenden på. Men ska alla moduler vara stabila? Självklart inte, vi vill ju att våra program ska kunna följa med ovärldens krav utan stora arbetsinsatser.
    • The Stable Abstractions Principle (SAP) - SAP är svaret på problemet som uppstår med SDP där det blir svårare och svårare att utföra förändringar desto fler beroenden modulen har. SAP säger att stabila moduler ska vara abstrakta, alltså inte ha någon implementation. Det ger oss frihet att förändra byta ut implementation utan att påverka modulen som alla andra beror på. Alltså ska de mest stabila modulerna vara abstrakta. Herr Matrin råkar ha en liten formel på att mäta detta också:
      Nc = Antalet klasser i modulen
      Na = Antalet abstrakta klasser/interface
      A = Abstrakthet(?) A = Na/Nc

      Om man jämnför A med den tidigare I så bör de följa varandra. En modul med högt I bör också ha ett högt N och en modul med lågt I bör ha lågt N.

      Det finns en ideallinje som I och A följer och man kan räkna på hur långt ifrån denna ideallinje en modul ligger med hjälp av följande formel:
      D = (A + I - 1) / sqrt(2) (ger ett värde mellan 0 - 0.7 ungefär) där värden närmare noll är bättre.

      Naturligtvis är detta bara beräkningar och inget man ska se som absoluta fakta. Men har man gjort värdena kan man välja att tro på dem eller säga att man har gjort val som gör dem missvisande. Men då har man ett värde att resonera runt vilket förmodligen är något bättre än att bara gissa.
    Resten av dokumentet handlar om designpatters som jag tror alla är representerande i GOF bok som heter Design Patterns.

    måndag 16 februari 2009

    Design Principles and Design Patterns

    Hittade ett kul dokument som Bob Matrin skriv i början av 2000-talet. Känner igen mycket av innehållet från Clean Code fast i kortare form. Tar upp några av sakerna där ifrån.

    Ruttnade kod
    Jag har inte varit med i något projekt där man inte har försökt att utgå från de bästa av principer och sett fram emot hur bra allt ska bli. Hur alla problem man har haft i tidigare projekt ska lösas och man ska göra allt rätt en gång för alla. Men när projektet har pågått ett tag så har de rosafluffiga drömmarna gått i kras och projektet är samma gamla ruttna soppa som de alltid blir. Man tycker att man borde ha lärt sig. En liten tröst i sammanhanget kan vara att de flesta utvecklingsprojekt tycks gå samma väga. Goda intentioner som malts sönder och samman. Bob tar upp fyra symtom på ruttnande design:
    • Stelhet inträffar när varje förändring tycks sluta i att man har skrivit om halva applikationen. En förändring kräver en förändring någon annanstans som i sin tur behöver ytterligare en uppsättning med förändringar. När man ser att även små förändringar får veckor att försvinna vill man inte gärna ändra ens när det behövs.
    • Skörhet inträffar när man utfört förändringar och man står inför en applikation som inte längre fungerar som den ska. Tillsynes helt orelaterade moduler slutar fungera pga ändringen. Vem vågar ändra när man vet att buggrapporterna kommer att hagla så fort man tagit applikationen i drift. Varje patch man gör tycks bara leda till fler problem än de fixade.
    • Orörlighet inträffar när man inte kan återanvända existerande kod trots att specifikationen borde passa som hand i handske. Det beror ofta på att modulen man ska använda har en stor ryggsäck i form av beroenden åt alla håll och kanter vars vikt överträffar vinsten i att återanvända den existerande funktionalliteten.
    • Tröghet kommer i två varianter, från design/arktektur och från utvecklingsmiljön. När trögheten kommer av design har man gjort vägval som kräver stora insatser om man ska göra "rätt". Då blir det ofta lätt att skriva ett hack eller två. Tröghet från utvecklingsmiljön kan vara långa kompileringstider. Då kan det tex vara frestande att checka in kod utan att ha kompilerat först. Dumt men frestande.
    Vad är orsakerna till dessa symtom då? Enligt Bob Martin är förändrade krav den stora källan till att symptomen ovan uppstår. För att det ska bli riktigt bra så kanske man har utvecklare som ska lösa en uppgift under tidspress utan att känna till hur den befintliga arkitekturen är tänkt att fungera. Bit för bit löses de ursprungliga goda intentionerna upp till en ohanterlig massa likt en sönderkokt potatis. Men vi utvecklare kan ju inte gärna skylla på förändrade krav. Vi vet att krav förändras.

    Lösningen för att hantera förändrade krav är att hantera de beroenden som finns i koden. Nedan kommer några av de principer som föreslås i dokumentet.

    Open closed principle (OCP)
    Alla "moduler" ska vara öppna för att byggas ut men stängda för att modifieras. Låter lite motsägelsefullt men är en av de viktigaste principerna för objektorienterad systemutveckling. Man ska kunna förändra vad modulen gör utan att behöva förändra modulens kod. Nedan kommer några tekniker för att hantera OCP.
    • Dynamic polymorphism - Går kort och gott ut på att man ska definiera ett gränssnitt, tex ett interface som man sedan kan lägga till implementationer som löser de specifika uppgifter som olika omständigheter.
    • Static polymorphism - Genom användning av templates eller generics kan man bygga ut funktionalliteten utan att behöva röra den existerande koden (exemplet i pappret känns lite gammalt och jag ser dem mer eller mindre som likadana).
    Genom att följa OCP kan vi skapa moduler som är lätta att bygga vidare på utan att man behöver ändra den existerande koden. Det är ett ideal som ibland kan vara svårt att nå men även om man bara delvis når målet är det något som gör din applikation betydligt enklare att underhålla. Det är alltid bättre om ändringar inte nästlar sig sin i existerande kod som fungerar.

    The Liskov Substitution Principle (LSP)

    Varje subklass ska kunna ersättas av sin basklass. Låter trivialt och med dagens moderna programmeringsspråk något som vi som vi får nästan gratis. Vi kan tex ta vilket objekt från en subklass och lägga det i en array typad för basklassen. Inget konstigt med det. Det finns dock några saker som vi fortfarande bör ha i bakhuvudet.
    • Cirkel/Oval dilemmat - Jag översätter detta till det något mer lätthanterliga Rektangel/Kvadrat. I skolan fick jag lära mig att en kvadrat är ett specialfall av en rektangel vars höjd och bredd råkar vara lika långa. Men så enkelt är det inte i programmeringens underbara värld. Om vi väljer att låta kvadrat vara en subklass till rektangel tvingar vi på en metod som en kvadrat inte har användning för. Höjd och bredd på en kvadrat är oviktigt, en längd på en sida räcker. Så för att kunna ha kvadraten som en subklass till rektangeln måste vi fuska till kvadraten så att om vi sätter höjden så kopierar vi det till bredden så att de alltid har samma värde. Men alternativet är inte mycket roligare. Om vi använder kvadraten som basklass kan vi inte manipulera rektangeln utan att behöva kolla vilken typ av objekt vi har och sedan kasta den till lämplig subtyp som vi sedan kan manipulera. Att ha rektangeln som basklass är lämpligare än kvadraten i detta fall. Nu tror vi att vi har tänkt på allt men betänk följande testmetod:

      public testSquare(){
      Rectangle rectangle = new Square();
      rectangle.setHeight(5);
      rectangle.setWidth(3);

      AssertEquals(rectangle.getHeight(), 5);
      AssertEquals(rectangle.getWidth(), 3);
      }

      Hur ska testet gå igenom om vi dolt baken kulisserna kopierar bredden till höjden på kvadraten för att lösa vår arvsheariki?
    Berättelsen ovan illustrerar att Rektangel enligt RSP är en dålig basklass för en Kvadrat, i alla fall om man vill sätta höjd och bredd. Om man sent i utvecklingscykeln hittar ett dyligt misstag kan det vara väldigt svårt att rätta till och man får börja skriva diverse mer eller mindre fula undantag från regeln vilket inte direkt gör koden lättare att förstå för den som ska läsa den vid en senare tidpunkt.

    The Dependency Inversion Principle (DIP)
    Många tekniker använder DIP (COM, CORBA, EJB för att nämna några). Om OCP är målet som ska uppnås kan man säga att DIP är den primära metoden att nå målet. DIP säger att man ska använda interface eller abstrakta klasser i sina gränsnitt i stället för att anropa de mer rörliga konkrekta klasserna direkt. Om man använder de konkreta klasserna är det svårt att ändra den underliggande funktionalliteten utan att behöva ge sig in och ändra i redan fungerande kod. Om det skulle uppstå en situation tex av licensskäl som innebär att du måste byta ut en existerande modul så ska du inte behöva ändra i din existerande kod utan bara plugga in den nya modulen mot det existerande gränssnittet. Genom att använda tex en Abstract Factory kan du dessutom i runtime bestämma vilket implementation av interfacet som ska användas.

    Det stora motivet för DIP är att skydda sig mot förändringar och jag tycker nog att man bör skriva så många klasser som möjligt mot interface, men inte alla. Där man kan lita på att det inte kommer att uppstå förändringar är det naturligtvis inte nödvändigt att förvänta sig förändring. Problemet är att det inte är så mycket man kan lita på här i världen. För en .Net-utvecklare kanske det verkar onödigt att dölja hanteringen av sökvägar bakom ett interface för att de alltid har börjat med C: men nu med Mono kanske det inte är så självklart längre.

    The Interface Segregation Principle (ISP)
    Tänk att du har en webtjänst med tio metoder. Denna webtjänst används av tre andra applikationer (klienter). Om du nu måste ändra i någon metod för att stödja ett nytt behov hos en av klienterna så måste du ändra på de andra två klienterna också för att de alla ser samma gränssnitt mot tjänsten. Ingen rolig tanke tycker i alla fall jag. En bättre metod hade varit att dölja din webtjänst bakom tre stycken gränssnitt som kan förändras oberoende av varandra. Om en ändring sker i webtjänsten så kommer den bara att synas för de som får tjänsten publicerad genom sitt klientgränssnitt.

    söndag 15 februari 2009

    T1-T9

    Kanske borde dela upp denna men det är de sista reglerna. De berör testning och ger råd om hur man ska förhålla sig till dem. Jag får skriva detta ganska närma vad som står i boken då min personliga erfarenhet med att jobba med tester inte är så hög jag skulle önska. Tyvärr är tester något som projektledare tror att det är okej att strunta i tron om att vi programmerare skulle leverera mera fungerande kod som kan tas i produktion.

    T1: Otillräkliga tester
    Det är en fråga som det är väldigt enkelt att ge ett enkelt svar på. Om det existerar vägar genom koden som inte har utforskats med hjälp av tester vet du inte att din kod fungerar som den ska. Framför allt vet inte andra utvecklare att de måste ta upp en webapplikation, skriva i sökfältet, dubbelklicka på listan och sedan ändra i postnumret och sedan spara för att han/hon ska se att just den funktionen fortfarande fungerar efter de sista ändringarna. Så även om du vet hur du ser att funktionen fungerar så är testerna även en specifikation hur funktionen ska fungera.

    T2: Använd test-täcknings verktyg
    Tog upp lite av detta på T1 men vi människor är inte gjorda för vissa typer av uppgifter. Använd därför ett verktyg för att påvisa vad som är testat och viktigare, vad som inte är testat.

    T3: Strunta inte i triviala tester
    De är enkla att skriva och värdet i form av dokumentation av hur de ska fungera är ovärderligt.

    T4: Ett ignorerat test är en fråga om tveksamheter
    Om du inte har skrivit ett test runt en funktion visar du att du inte har förstått hur funktionen ska fungera och lämnar dessutom de andra utvecklarna med ett stort frågetecken om hur funktionen ska uppföra sig.

    T5: Testa gränsfall
    Vi har gått igenom gränsfall tidigare. Att testa att gränsfallen fungerar är extra viktigt. Att metoden fungerar i de flesta fall är inte tillräkligt.

    T6: Testa mer runt buggar
    Det finns ett ordspråk som säger att en olycka sällan är ensam. Det samma gäller för utvecklare. En trött utvecklare som har missförstått något har förmodligen inte bara gjort ett fel. Skriv inte bara ett test för att verifiera buggens existens. Undersök testtäckningen på näraliggande kod och se till att den är testad. Det är ett betydligt effektivare sätt att upptäcka fel än att invänta nästa bugg och åter igen sätta sig in i vad det är koden antas leverera.

    T7: Se mönster för varför tester falerar
    Om du har ett stort antal tester kan man ofta se mer mönster som man inte har förstått att man måste testa för. Tex kanske man har en begränsning på hur lång en textsträng får vara som man inte var medveten om när man skrev testerna initialt.

    T8: Testtäckning påvisar mönster
    Varför man testat viss kod men inte annan kod kan visa på intressanta mönster hur utvecklarna tänker. Att viss kod kanske anses vara så stabil att man inte behöver testa på den kan vara en källa till att andra tester falerar. För man vet ju trots allt inte att koden fungerar.

    T9: Tester ska vara snabba
    Detta är nog den viktigaste reglen för testerna. Om inte testerna är snabba är det ingen som vill köra dem. Det är också den enskilt viktigaste anledningen till varför man inte ska använda xUnit (jUnut, nUnit etc) till att skriva integationstester som springer igenom hela applikationen. Desto mindre varje enskilt test testar desto snabbare är det och desto mindre är risken att någon utvecklare tröttnar på att vänta på dem och slutar att köra testerna.

    lördag 14 februari 2009

    N1 - N7

    Nu när vi är klara med de generella reglerna så tittar vi på de som rör namnsättning.

    N1: Använd beskrivande namn
    Vi har alla sett kod som ser ut ungefär så här:

    public void doCalculation(){
    i = j + k;
    }

    Det är kod där man kan fråga sig vad för beräkning doCalculation gör, letar upp den sista siffran i PI? Variablerna i, j och k säger inte heller så mycket om varför de ska finnas. Bättre då hade varit att skriva metoden så här (om nu metoden skulle beräkna löner)

    public void calculateTotalSalary(){
    totalSalary = baseSalary + overTime;
    }

    Här behöver ingen fundera på vad vare sig variablerna eller metoden har för syfte.

    N2: Välj rätt namn på rätt abstraktionsnivå
    Denna är lite lurig. När ska man kalla ett telefonnummer för telefonnummer och när är det en kopplingssträng eller riktnummer? Att fundera på hur abstrakt namnsättningen ska vara är oerhört viktigt då det begränsar fantasin på läsaren om vad en funktion kan ha för användningsområde. Kopplingssträng skulle kunna utnyttja vilken tjänst som helst (Skype, telefon eller två burkar med snöremellan. Telefonnummer har begränsat användningsområdet till bara telefonnummer. Ibland är det rätt att använda väldigt specifika namn på saker och ting och ibland är det bra att vara öppen för att det kan komma andra implementationer än den man ska skriva just nu. Att välja rätt är inte enkelt men oerhört viktigt. Fundera ett varv extra på varje namn om det är tillräkligt generellt eller inte.

    N3: Använd standardiserad vokabulär
    Att använda ord för företeelser som alla känner igen är viktigt i alla språk. Kebab med deg kanske går att få till pizza med lite fantasi men det hade varit lättare att förstå om jag sagt pizza direkt. Om det heter webcontroll eller taglib är i sig självt oviktigt. Vad de som ska läsa din kod känner till det som är däremot viktigt. Om man pratar om en AbstractFactory se då till att det är definitionen från GOF du hänvisar till för det är sannolikt den som de flesta känner till. Om du gör ett projekt för styrning av mjölkmaskiner se då till att du använder ord från den domänen i stället för att hitta på egna uttryck. I stora projekt kan man kanske tillåta sig att ha en uppsättning med egna uttryck för ett antal företeelser som är specifika för projeket men i övrigt använd uttryck som alla kan googla definitionen på.

    N4: Otvetydiga namn
    När man döper saker och ting ska man vara nogrann med att metoden inte kan tolkas till att den gör något annat än den gör. Metodnamn som uttrycker sig i svepande ordalag, tex rename säger inte så mycket om vad det är som ska döpas om eller hur objektet ska döpas om. Så var tydlig.

    N5: Använd långa namn på stora "scope"
    Att använda en "i" som ett variabelnamn kan vara ok för väldigt korta metoder där betydelsen av "i" kan framgå tydligt ändå. Men i större sammanhang behöver man längre mer beskrivande namn som klarar av att beskriva den komplexitet som kommer med större sammanhang.

    N6: Koda inte koden
    Detta är min favorit sedan jag började programmera i .Net-miljö där standarden (i alla fall på min arbetsplats) är att skriva variabler med m_ och s_ och prefixa interface med I. För mig är detta Ungersk kodning som är precis lika illa som btnMyTextStr. Om man nödvändigtvis måste koda sina variabler gör det som ett suffix och så långt bort från de publika gränssnitten som möjligt, tex på ett interface Company där den implementerande metoden skulle kunna kodas som CompanyImpl. Fast det bästa är om man kan ge riktiga namn utan att behöva lägga på koder. Unvik den Ungerska sjukan med all kraft.

    N7: Namn ska beskriva sidoeffekter
    Har ni stött på en metod som ser ut något i still med detta:

    public void setName(String name);

    Men i stället för att bara sätta ett namn så sparas objektet också i databasen. Om du måste göra metoder som gör två saker, se då till att det framgår av namnet. SetNameAndSave skulle ha varit ett bättre namn som inte ger utvecklarna några överraskningar.

    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.

    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.

    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.

    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.

    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.

    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:
    • 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)