PSWinCom ansetter en ekte .NET Ninja

Jepp, målet er nådd, for nå omtales jeg som en “ekte .NET ninja” :D Her er et utklipp fra min nye arbeidsgivers siste nyhetsbrev:

En ekte .NET Ninja!
Klikk for full størrelse..

Man kan få prestasjonsangst av langt mindre! Er du interessert i å lese hele nyhetsbrevet, kan du laste det ned her. Vær tålmodig, det kan ta litt tid.


Vinner av fantasy premier league for andre året på rad

Advarsel: Selvskryt følger..

Som jeg gjorde i fjor vant jeg også i år den private Fantasy Premiere League konkurransen mellom mine kollegaer.

Contiki Winners 08/09

Nytt av året var at alle managere i Fantasy Premiere League automatisk ble med i sin egen serie, kalt Head to Head. Og jammen vant jeg ikke den også. Man skulle nesten tro jeg hadde peiling på engelsk fotball.

Head to Head 08/09

Som du kan se i den første tabellen var vi bare fire kolleger/venner som spilte 08/09-sesongen. Når den nye sesongen starter i høst håper jeg vi kan bli noen flere, så hvis du er interessert så må du skrike ut. Lurer du på hva det hele dreier seg om så finner du Fantasy Premiere League her.


Snookiepoof på google code

Snookiepoof er et lite C# prosjekt jeg har jobbet med de siste ukene, hvor jeg eksperimenterer med ting som OO-design patterns og clean code. Jeg planlegger å skrive endel blogposter fremover basert på erfaringer med dette prosjektet.

Snookiepoof er en motor for rollespill-aktige spill, hvor man beveger seg rundt i en verden og oppdager ting man kan gjøre. Foreløpig inneholder løsningen et konsoll-basert interface, men designet skal også kunne støtte andre typer brukergrensesnitt.

Jeg bruker TDD/BDD under utvikling, og legger fokus på gode abstraksjoner og å følge SOLID-prinsippene. Command-pattern er mye brukt, og spillmotoren inneholder bl.a. min implementasjon av en generisk state machine.

Av eksterne avhengigheter kan jeg nevne at jeg har brukt TinyBDD til å skrive de fleste testene, og bruker Ninject for dependency injection. Jeg planlegger også å bruke db4o for persistering.

Jeg har lagt ut prosjektet på Google Code, så hvis du er interessert i å ta en titt så har du muligheten til det.

Knagger:


Å bruke av egen fritid

Robert C. Martin hadde æren av å holde Keynote-talen på NDC i år. Der snakket han om hvilke prinsipper man bør følge for å kunne kalle seg en profesjonell utvikler. De som har fulgt med på denne bloggen vet at jeg lytter mye til denne mannen, og jeg er enig i det meste han sier.

Flere av prinsippene han presenterte gikk på hvordan vi utviklere bør forplikte oss til å bli bedre. Her er et utvalg:

Kjenn ditt fagfelt - Vi utviklere må kunne utviklingsfaget. Vi må strekke dette til områder utenfor akkurat det vi holder på med og har erfaring med. Vi må lære vår historie, ulike utviklingsmetodikker og prinsipper som har vært brukt opp igjennom vår forholdsvis korte historie - hvorfor de virket, og hvorfor de har blitt endret eller erstattet.

Kjenn ditt domene - Vi utviklere må sørge for å bli eksperter innenfor det forretningsområdet vi jobber i. Skal vi kunne produsere gode løsninger kommer vi ikke utenom at vi må kunne forretningen og forstå brukerne. Dette har jeg vært inne på i blogposten Utviklerprofiler og fire ferdighetskategorier.

Kontinuerlig læring - Vi må sørge for å hele tiden utvikle oss. Programmering er et stort fagfelt, og en utdanning pluss noen års erfaring er bare et lite steg på veien til å bli en mester. Faget er også i kontinuerlig utvikling, og mens mye bygger på kunnskap som allerede er etablert, er vi nødt til å henge med for å kunne utvikle gode løsninger i morgen.

Øv mye - For å bli en god fotballspiller må man øve. For å bli en god musiker må man øve. Det nytter ikke å bare spille kamper eller konserter. Å basere seg på å bare gjøre produksjonskode holder heller ikke. Man må øve på nye tegnikker, og man må repetere gamle for å holde dem ved like. Man må trimme hjernen, og få ting til å sitte i fingrene. Kun da kan man gjøre den beste jobben når det virkelig gjelder.

Og så slipper Uncle Bob den store bomben:

“En profesjonell utvikler bruker 20 timer av sin fritid hver uke på å utvikle seg selv og bli bedre.”

Rundt meg merket jeg at folk reagerte. 20 timer? Av fritiden vår? Nei nå må han roe seg ned.

Jeg har selv brukt omtrent så mye tid av egen fritid på å bli en bedre programmerer det siste halve året (se å bli en .net ninja), men det er faktisk ganske mye, og man må ofre endel for å få det til. Jeg er på god vei til å bli en mye bedre utvikler enn jeg var, og ideelt sett er jeg enig at det hadde vært fint om alle gjorde som jeg har gjort, men det er nok ikke realistisk å få det til (med den innstillingen vi har til jobb i norge i dag).

Fordeling av tid pr uke
Omtrent slik ser uken min ut..

Jeg er enig med Uncle Bob i to ting: For det første må utviklere generelt jobbe mye mer med å bli bedre enn tilfellet er i dag. Og for det andre er jeg enig i at det er utviklernes eget ansvar å sørge for at de utvikler seg. Men våre arbeidsgivere må legge til rette for at utviklingen kan finne sted.

Vår industri må endre sin holdning til hva som er “godt nok”. Vi må innse at vellykket softwareutvikling er mye mer komplisert enn å sette en gjeng halv-erfarne utviklere til å produsere kode. Vi må skape et miljø hvor utvikling av de ansatte er en helt naturlig del av forretningen.

Man må sette av tid til å eksperimentere, og til å øve på grunnleggende ferdigheter. Systemutvikling har mye mer til felles med lege- og advokatyrket, yrker med ekstremt høy kompleksitet og endring, enn til mer forutsigbare yrker som f.eks. bygningsarkitekt eller tradisjonelle ingeniøryrker (flame on..).

Ja, en programmerer må være interessert i å kontinuerlig utvikle seg og sine ferdigheter, og en dyktig programmerer bruker også noe av sin fritid på dette. Men vi må skape større rom og forståelse for at dette skal skje også innenfor arbeidstiden. Ikke bare det å holde seg oppdatert, men å kontinuerlig bli bedre, bør være endel av stillingsinstruksen til enhver utvikler.

Knagger:


null-referanser

NullReferenceExceptionPå flytoget etter NDC 2009 hadde mine kollegaer og jeg en nokså heftig diskusjon om bruk av null. Det var et nokså uskyldig spørsmål som startet det hele:

“Sjekker du alltid om parametrene som sendes inn til en metode er null, og kaster ArgumentNullException om så er tilfelle?”

Mitt svar var ganske bestemt: Nei, det gjør jeg stort sett aldri! For meg er det å sjekke for null noe man gjør i frykt, noe man gjør for å kompansere for dårlig kode. I de fleste tilfeller er det en bug å sende inn null, og jeg ønsker ikke å legge til rette for sloppy kode ved å sette på støttehjulene og teste for dette.

Kode som forventer en instans men får null vil kaste en NullReferenceException, som er mer enn godt nok for kode som er godt skrevet - den energien en eventuelt putter inn i å lage mer detaljerte feilmeldinger er ikke verdt det i dette tilfellet.

Videre hevdet jeg at null i seg selv er en uting. Kode som er skrevet slik at man gjentatte ganger må teste for null er ikke god kode. Å teste for null er en code smell. Bruk av null hører ikke hjemme i kode som befinner seg andre steder enn på det laveste abstraksjonsnivået - null er ikke noe forretningsfolk kan snakke om, og bør derfor etter min mening heller ikke eksistere i kode som beskriver forretningskonsepter.

Null Object pattern

Problemet jeg snakker om kan illustreres med følgende kodesnutt:

Employee e = DB.GetEmployee(“T-Man”);

if (e != null && e.IsTimeToPay(DateTime.Today))

    e.Pay();

Siden databasen kan returnere null om “T-Man” ikke eksisterer benytter vi oss av det faktum at det første uttrykket i if-testen evalueres først, og det andre uttrykket kun om det første er sant. Vi har alle skrevet kode som dette her. Vi har også alle glemt å teste for null fra tid til annen - selv om dette er et vanlig mønster så er det både uelegant og noe som fører til mange feil.

I stedet for å returnere en null-referanse kunne vi la databasen kaste en exception, men da måtte vi ha brukt try/catch ved alle kall, noe som er enda mindre elegang.

Et alternativ til å bruke null-referanser er å implementere et objekt som har det forventede grensesnittet, men som ikke gjør noen ting. Dette kaller vi for Null Object. Min kollega hevdet at dette er veldig tungvindt å implementere, men etter å ha tenkt meg litt om så finner jeg mange steder hvor jeg har brukt det selv, og hvor det er langt fra vanskelig å implementere, samtidig som det gjør koden som bruker objektene langt enklere.

Her er et eksempel fra prosjektet jeg holder på med akkurat nå. Brukeren skriver inn noe tekst som blir gjort om til en UserCommand. Poenget er å unngå å returnere null, så hvis teksten som sendes inn er gibberish så returnerer jeg en UnknownCommand:

public UserCommand GetCommand(FirstPerson person, string[] commandArguments)

{

    if (ValidCommandArguments(commandArguments))

    {

        return _commandDictionary 

            .GetParser(commandArguments[0])

            .GetCommand(person, commandArguments);

    }

    return new UnknownCommand();

}

UnknownCommand er et null-objekt: Det implementerer UserCommand-interfacet, men gjør ingen ting. Klienter av UserCommand kan bruke dette objektet på samme måte som alle andre commands - jeg trenger ikke kaste noen exceptions, og aldri teste for null.

public class UnknownCommand : UserCommand

{

    public UserCommandResult Execute()

    {

        return new UserCommandResult

        {

            Success = false,

            Text = “Did not compute!”,

        };

    }

}

Jeg har flere eksempler som ligner på dette. Der hvor jeg bruker strategy pattern i kombinasjon med en eller annen form for en factory, implementerer jeg nesten alltid også et null-objekt, en dummy-klasse som kan brukes i stedet for en virkelig strategi.

Dette beviser ikke at null objects kan brukes over alt, ei heller at det er trivielt, men jeg er ganske sikker på at bruk av dette mønsteret vil føre til kode som er enklere å lese og vedlikeholde, og som er mer feilfri.

Kan vi erstatte != null testen?

Men det er ikke til å komme bort ifra at man av og til må bruke null-referanser. Det er også et faktum at jeg hater å skrive != og == null. Jeg ønsker derfor en mer deskriptiv måte å teste på - en som ikke bryter abstraksjonsnivået. Dette er hva jeg har kommet frem til:

[Test]

public void Test()

{

    object o = null;

 

    if (o.IsNotSetYet())

        Assert.That(o == null);

        //Cool, I called a method on a null reference

    else

        Assert.Fail(“o was not null”);          

}

Denne testen feiler ikke! Men hvordan er det mulig å kalle en metode på en null-referanse? Jo, det er mulig takket være extension methods:

public static class ObjectExtensions

{

    public static bool IsNotSetYet(this object o)

    {

        return o == null;

    }

    public static bool IsSet(this object o)

    {

        return o != null;

    }

}

Er det ikke vakkert?


En generisk state machine

TurnstileFinite state machines (FSM), tilstandsmaskiner på norsk, er blant de mest kraftige abstraksjonene vi programmerere har tilgjengelig, og har et stort og variert bruksområde. FSM gir oss en enkel og elegant måte å utforske og definere oppførselen til komplekse systemer, og implementasjonen er både enkel å fortså og enkel å modifisere. I boken sin Agile Principles, Patterns, and Practises in C# sier Robert C. Martin følgende om dette:

“Finite state machines are underutilized. In many scenarios their use would help to create cleaner, simpler, more flexible, and more accurate code.”

I den enkleste formen er en state machine gjerne implementert som en eller flere switch/case statements - i mer komplekse løsninger kan man bruke STATE pattern. I .NET 3.0 fikk vi også Windows Workflow Foundation, hvor vi kan designe State Machine Workflows.

Men alle disse alternativene er mere komplekse enn å lage en FSM basert på en overgangstabell. I denne artikkelen designer jeg en generisk FSM-motor som kan gjenbrukes i mange scenarier. For å illustrere hvordan den virker vil jeg implementere tilstandsmaskinen som finnes i en typisk passeringsautomat - slike som man finner på T-banestasjoner, flytoget osv.

I diagrammet under ser du tilstandene (states), overgangene (transitions), hendelsene som kan inntreffe (transition conditions) og det systemet skal utføre når det går fra en tilstand til en annen (actions) i en slik passeringsautomat.

FSM_turnstile

Ta for eksempel en automat som i utgangspunktet er låst. Hvis noen putter en mynt på automaten, vil systemet kalle en rutine for å låse opp porten, og endre statusen til å være ulåst. Når den så registrerer at noen passerer porten, vil den kalle rutinen for å låse porten, og returnere til den låste tilstanden. Hvis noen passerer mens systemet er i låst tilstand, vil automaten kalle alarm-rutinen og forbli i låst tilstand.

Skulle noen finne på å putte på penger mens systemet er ulåst, vil automaten si “tusen takk”, og forbli i ulåst tilstand.

Det første jeg vil gjøre er å opprette et par enumerasjoner som beskriver systemets tilstander og hendelser:

    9 public enum State { LOCKED, UNLOCKED }

   10 public enum Event { COIN, PASS }

Jeg utviklet selvsagt automaten vha. TDD, og her følger oppsettet av testklassen, som viser hvordan en StateMashine opprettes og konfigureres:

    9 using StateMachine = Marosoft.Generics.StateMachine<State, Event>;

   10 

   11 [TestFixture]

   12 public class TestGenericStateMachine

   13 {

   14     private StateMachine _stateMachine;

   15 

   16     static bool _unlockCalled = false;

   17     static bool _alarmCalled = false;

   18     static bool _thankYouCalled = false;

   19     static bool _lockActionCalled = false;

   20 

   21     private Action unlock = () => _unlockCalled = true;

   22     private Action alarm = () => _alarmCalled = true;

   23     private Action thankYou = () => _thankYouCalled = true;

   24     private Action lockAction = () => _lockActionCalled = true;

   25 

   26     [SetUp]

   27     public void SetUp()

   28     {

   29         _stateMachine = new StateMachine(State.LOCKED);

   30         // Equivalent to generic specification due to using statement

   31 

   32         _stateMachine.AddTransition(State.LOCKED, Event.COIN, State.UNLOCKED, unlock);

   33         _stateMachine.AddTransition(State.LOCKED, Event.PASS, State.LOCKED, alarm);

   34         _stateMachine.AddTransition(State.UNLOCKED, Event.COIN, State.UNLOCKED, thankYou);

   35         _stateMachine.AddTransition(State.UNLOCKED, Event.PASS, State.LOCKED, lockAction);

   36     }

Legg merke til det spesielle using direktivet på linje 9. Dette er ikke et krav, men lar meg definere en StateMachine-variabel på linje 14 og initiere den på linje 29 uten å spesifisere hvilke enumerasjoner som skal brukes. Dette er et generelt tips om hvordan man kan gjøre kode som bruker mye generics mer lesbar.

Linje 16 til 24 setter opp fire dummy-actions som skal brukes når jeg tester tilstandsmaskinen. Det mest interessante kommer først på linje 32, hvor jeg begynner å definere tilstandsovergangene.

Du kan lese linje 32 på følgende måte: Hvis nåværende tilstand er LOCKED, og noen putter på en COIN, så skal tilstanden endres til UNLOCKED, og “unlock” skal kjøres.

Den første testen jeg trenger sjekker bare den initielle tilstanden, som jeg satte i konstruktøren. Testen er ikke mer enn én linje:

   40 [Test]

   41 public void InitialConditions()

   42 {

   43     Assert.AreEqual(State.LOCKED, _stateMachine.State);

   44 }

Deretter skriver jeg tester for hver av de fire mulige overgangene. I hver test setter jeg først gjeldende status, noe som bare er aktuelt i enhetstestene - i virkelig bruk vil man ikke overstyre status på den måten. Deretter sender jeg inn et event til tilstandsmaskinen, før jeg sjekker ny status og om riktig action har blitt kalt (ref oppsettet av test-klassen). Her er testene:

   46 [Test]

   47 public void CoinInLockedState()

   48 {

   49     _stateMachine.State = State.LOCKED;

   50     _stateMachine.HandleEvent(Event.COIN);

   51     Assert.AreEqual(State.UNLOCKED, _stateMachine.State);

   52     Assert.IsTrue(_unlockCalled);

   53 }

   54 [Test]

   55 public void CoinInUnlockedState()

   56 {

   57     _stateMachine.State = State.UNLOCKED;

   58     _stateMachine.HandleEvent(Event.COIN);

   59     Assert.AreEqual(State.UNLOCKED, _stateMachine.State);

   60     Assert.IsTrue(_thankYouCalled);

   61 }

   62 [Test]

   63 public void PassInLockedState()

   64 {

   65     _stateMachine.State = State.LOCKED;

   66     _stateMachine.HandleEvent(Event.PASS);

   67     Assert.AreEqual(State.LOCKED, _stateMachine.State);

   68     Assert.IsTrue(_alarmCalled);

   69 }

   70 [Test]

   71 public void PassInUnlockedState()

   72 {

   73     _stateMachine.State = State.UNLOCKED;

   74     _stateMachine.HandleEvent(Event.PASS);

   75     Assert.AreEqual(State.LOCKED, _stateMachine.State);

   76     Assert.IsTrue(_lockActionCalled);

   77 }

Nå bør du skjønne hvordan StateMachine skal brukes, og jeg er klar for å vise hvordan den er implementert. Her er første bit; StateMachine har en generisk definisjon som forventer to typer som skal representere henholdsvis statuser og eventer: Jeg har kalt dem TState og TEvent. Construktøren tar inn initiell status:

    9 public class StateMachine<TState, TEvent>

   10 {

   11     ///

   12     /// Setter is left internal for testing purposes

   13     ///

   14     public TState State { get; internal set; }

   15 

   16     public StateMachine(TState initialState)

   17     {

   18         State = initialState;

   19     }

Videre inneholder klassen en liste med overganger (linje 21). En overgang - Transition - er en intern klasse i StateMachine (linje 23). Det er ikke så ofte jeg bruker private klasser i andre klasser, men her passet det fint. AddTransition-metoden (linje 31) som jeg brukte i SetUp i testklassen oppretter en ny Transition og legger den til i listen.

   21 private List<Transition> _transitions = new List<Transition>();

   22 

   23 private class Transition

   24 {

   25     public TState StartState;

   26     public TEvent Trigger;

   27     public TState EndState;

   28     public Action Action;

   29 }

   30 

   31 public void AddTransition( TState startState,

   32                             TEvent trigger,

   33                             TState endState,

   34                             Action action)

   35 {

   36     _transitions.Add(new Transition

   37     {

   38         StartState = startState,

   39         Trigger = trigger,

   40         EndState = endState,

   41         Action = action,

   42     });

   43 }

Og da gjenstår bare HandleEvent-metoden, som er gjengitt nedenfor. Den finner riktig overgang basert på event-parameteren og gjenldende status. Til dette bruker jeg Find-metoden til List. Hvis en overgang finnes, brukes den til å sette ny status, og riktig action kalles (linje 55 og 56).

Hvis et ugyldig event sendes inn for gitt status (ikke mulig for denne passeringsautomaten, men kan tenkes for andre tilstandsmaskiner), så kaster jeg et ArgumentException.

   45 public void HandleEvent(TEvent e)

   46 {

   47     var transitionToPerform

   48         = _transitions.Find((transition)

   49         => transition.StartState.Equals(State) && transition.Trigger.Equals(e));

   50 

   51     if (transitionToPerform == null)

   52         throw new ArgumentException(

   53             string.Format(“Can not handle event {0} in state {1}.”, e, State));

   54 

   55     State = transitionToPerform.EndState;

   56     transitionToPerform.Action.Invoke();

   57 }

Min StateMachine er en forbedring av Robert C. Martins Transition Tables-eksempel fra boken jeg refererte. Ved å bruke generics har jeg gjort den gjenbrukbar. Når du trenger en tilstandsmaskin behøver du bare definere tilstandene og eventene, og konfigurere StateMachine med de riktige overgangene.

Det er også verdt å merke seg at du ikke er begrenset til å bruke enumerasjoner til å definere tilstander og/eller eventer; du kan like gjenre bruke klasser, slik som jeg har gjort her:

   12 public abstract class TurnstileState

   13 {

   14     public override bool Equals(object obj)

   15     {

   16         return base.GetType().Equals(obj.GetType());

   17     }

   18     public override int GetHashCode()

   19     {

   20         return base.GetType().GetHashCode();

   21     }

   22 }

   23 public class LockedState : TurnstileState { }

   24 public class UnlockedState : TurnstileState { }

Den abstrakte klassen TurnstileState (turnstile er engelsk for passeringsautomat) definerer et interface (et tomt et sådan) for en tilstand. LockedState og UnlockedState arver fra TurnstileState, og erstatter Event.LOCKED og Event.UNLOCKED. Jeg har overstyrt Equals fordi HandleEvent bruker Equals til å finne riktig overgang (merk at man også alltid skal overstyre GetHashCode når man overstyrer Equals).

Flytende konfigurering..

Til slutt gjorde jeg en liten forbedring i hvordan jeg oppretter overgangene. Flytende interfacer er veldig populært for tiden, og kan gjøre kode mer lesbar. Sammenlign SetUp-metoden nedenfor med den jeg presenterte tidligere, og se om du er enig i at dette er bedre.

   24 [SetUp]

   25 public void SetUp()

   26 {

   27     _stateMachine = new StateMachine<State, Event>(State.LOCKED);

   28 

   29     _stateMachine.Configure()

   30         .Given(State.LOCKED)

   31             .When(Event.COIN)

   32                 .ThenSetState(State.UNLOCKED).AndRun(unlock)

   33             .When(Event.PASS)

   34                 .ThenSetState(State.LOCKED).AndRun(alarm)

   35         .Given(State.UNLOCKED)

   36             .When(Event.COIN)

   37                 .ThenSetState(State.UNLOCKED).AndRun(thankYou)

   38             .When(Event.PASS)

   39                 .ThenSetState(State.LOCKED).AndRun(lockAction);

   40 }

Da jeg implementerte det som trengtes for å få denne setup-metoden til å kjøre oppdaget jeg noe ganske morsomt. Konfigurerings-objektet jeg opprettet er faktisk en tilstandmaskin - en Finite State Machine. Begynner du først å tenke på denne måten så oppdager du stadig nye tilfeller hvor FSM-teknikken kan benyttes.

Tilstandsmaskiner kan gjøre koden din enklere å forstå, vedlikeholde og endre, og bør ikke undervurderes. De fjerner “spagetti”, og lar dine øvrige klasser konsentrere seg om sine primære ansvar. Tenk gjennom hvilke tilstander koden din kan ha neste gang du programmerer, og vurder å bruk en tilstandsmaskin for å forenkle koden.

Knagger: , ,


Min nye bibel innen Smidig Design

Agile Principles, Patterns, and Practices in C#Robert C. Martin’s Agile Principles, Patterns, and Practices in C# er DEN BESTE programmeringsboken jeg noen gang har lest!!!

Det er ikke så ofte man kan begynne en blogpost med en setning som det der, og bruke både all-CAPS og tre utropstegn uten å få en flau smak i munnen. Men denne boken fortjener det virkelig. Den gir deg både lyst og kunnskapen du behøver for å omforme deg selv til en ideell, smidig systemutvikler.

Boken er full av anekdoter som virkelig får deg til å fortså hva som er galt med mange prosesser i dag, og hva som kan gjøres bedre. Og den utgjør bortimot et komplett sett med teknologi-uavhengig kunnskap om hvordan du skal gå fra en diffus problemstilling til et fungerende produkt.

Section I: Agile development

Den første delen av boken handler om smidig utvikling. Bob presenterer hovedtrekkene i eXtreme Programming, og går spesielt i dybden på planlgging, testing og refakturering. Han presenterer også en veldig bra historie som viser hvordan parprogrammering kan fungere i praksis:

“In order to demonstrate XP practices, Bob Koss and Bob Martin will pair program a simple application while you watch like a fly on the wall. We will use test first design and a lot of refactoring to create our application. What follows is a faithful re-enactment of a programming episode that the two Bob’s actually did.”

Historien er også tilgjengelig online, riktignok implementert med Java-kode - du kan lese den her.

Section II: Agile design

Her begynner boken virkelig å ta av. Bob snakker først om hvorfor design “rotner” over tid, og bruker resten av del 2 til å forklare hvordan man skal unngå at dette skjer. Han går i dybden på en rekke prinsipper innen objektorientert design, de såkalte SOLID prinsippene, og selv om jeg hadde hørt og lest en god del om dem fra før var det først med denne boken at jeg virkelig begynte å forstå.

Han forklarer hvorfor prinsippene er viktige, hvordan man skal kode for å innfri dem, og ikke minst hvilken tradeoff det innebærer å følge dem. Dette siste inpirerte meg til å skrive en liten blogpost jeg kalte når skal man følge SOLID prinsippene.

Bob bruker også mye av del 2 til å snakke om UML, og bruk av design-diagrammer innenfor smidig utvikling. Mange er av den oppfatning at modellering er noe man ikke skal gjøre når man er agile, men det kan være både feil og farlig. Bob lærer deg all den UML’en som er nødvendig for en smidig utvikler, og mener mye om hvordan man skal bruke dette “verktøyet”.

Section III: The Payroll case study

I del 3 demonstrerer Bob hvordan man går fra noen diffuse krav til et komplett program. Han bruker TDD, og viser deg all koden han produserer - inkludert testene. Han forteller grundig hvordan han tenker, og vi får se hvordan han følger OO-prinsippene og hvordan koden utvikler seg over tid.

I denne delen presenteres et hav av design patterns / mønstre, den mest forståelige forklaringen av design patterns jeg har vært borti faktisk, og vi lærer hvordan de brukes for å tilfredstille de grunnleggende prinsipene. Med fare for at den som leser boken blir pattern happy lærer han bort Command, Template Method, Strategy, Adapter, Monostate, Null object, Visitor, Decorator, Gateway, Proxy, State, osv., osv.

Denne delen av boken inspirerte meg til å skrive om hvordan man bryter avhengigheter mellom klasser, hvor jeg benyttet Strategy og Adapter.

Section IV: Packaging the Payroll System

I del 4 fortsetter man med applikasjonen fra del 3. Bob forteller oss om hvordan man bryter opp et prosjekt i moduler basert på Cohesion og Coupling. Han presenterer flere prinsipper, og patterns for å understøtte disse prinsippene. Og han fortsetter å presentere enhetstester og kode.

Denne delen inpirerte meg til å kode og blogge om hvordan man kan bruke en tilstandsmaskin til å gjøre kode mer presis og fleksibel: En generisk state machine i C#.

Mot slutten implementerer han også et grafisk brukergrensesnitt, og bruker da WinForms og Model View Presenter-mønsteret (MVP). Dette er det grundigste og mest forståelige eksempelet på MVP jeg noen gang har sett.

Det kan tenkes at jeg leste denne boken på akkurat det riktige tidspunktet i min egen utvikling, og at den ikke vil passe like godt for alle. Jeg tør likevel å anbefale den på det sterkeste til alle utviklere som ikke allerede praktiserer best practices innen smidig utvikling (TDD, parprogrammering, etc.) og som ikke allerede er guruer innen objektorientert design og vedlikeholdbar kode.

Andre Uncle Bob-relaterte blogposter: En karriære innen programmering | For mange utviklere som ikke bryr seg

 


Overvåk ledig diskplass med WMI

Med gjevne mellomrom går en eller annen disk på en eller annen server full, enten det er på grunn av ivrige logger, akkumulerende backups, voksende databaser eller noe helt annet. Uansett hva det er skaper det alltid problemer og tapt arbeidstid. Og med like gjevne mellomrom kan man høre meg klage, og si at nå vi få på plass en løsning som kan overvåke diskene våre.

I dag bestemte jeg meg derimot for å ikke klage lengre, og 20 minutter senere hadde jeg laget en liten app som viste meg hvor mye ledig diskplass det var på serverne våre. Jeg ante ikke at det skulle være enkelt.

Jeg begynte med å lage en liten dataklasse for å holde på informasjonen om en disk. Med hjelp av CodeRush tok det ikke mange sekunder eller tastetrykk. Her er DiskInfo-klassen:

    8 public class DiskInfo

    9 {

   10     public string DriveLetter { get; set; }

   11     public string VolumeName { get; set; }

   12     public double Size { get; set; }

   13     public double Free { get; set; }

   14     public double PercentFree { get { return Free / Size; } }

   15 

   16     public override string ToString()

   17     {

   18         return string.Format(“{0}{1}{2}{3}{4}”,

   19             DriveLetter.PadRight(5),

   20             VolumeName.PadRight(30),

   21             Size.ToString(“###,###,###,###”).PadLeft(15),

   22             Free.ToString(“###,###,###,###”).PadLeft(15),

   23             PercentFree.ToString(“#0%”).PadLeft(10));

   24     }

   25 }

Og så trengte jeg noe som kunne hente ut informasjonen. Jeg har hørt mye om Windows Management Instrumentation (WMI), men aldri brukt det. Men en liten titt på System.Management namespacet avslørte at dette var akkurat det jeg behøvde:

Provides access to a rich set of management information and management events about the system, devices, and applications instrumented to the WMI infrastructure. Applications and services can query for interesting management information (such as how much free space is left on the disk, what is the current CPU utilization, which database a certain application is connected to, and much more)…

Jeg laget en kjapp liten command-klasse jeg valgte å kalle DiskInfoRetriever, som gitt navnet på en server henter ut informasjon om alle diskene på den maskinen:

    9 using System.Management;

   10 

   11 public class DiskInfoRetriever

   12 {

   13     private string _Server;

   14 

   15     public List<DiskInfo> Items { get; set; }

   16 

   17     public DiskInfoRetriever(string server)

   18     {

   19         _Server = FixServerName(server);

   20         Items = new List<DiskInfo>();

   21     }

   22 

   23     private string FixServerName(string server)

   24     {

   25         return server.StartsWith(@”\\”)

   26             ? server

   27             : @”\\” + server + @”\root\cimv2″;

   28     }

   29 

   30     public void Execute()

   31     {

   32         var scope = new ManagementScope(_Server);

   33         var query = new ObjectQuery(

   34               “select FreeSpace, VolumeName, Size, Name “

   35             + “from Win32_LogicalDisk where DriveType=3″);

   36 

   37         var searcher = new ManagementObjectSearcher(scope, query);

   38         var results = searcher.Get();

   39 

   40         Items.Clear();

   41 

   42         const int MEGABYTE = 1048576;

   43         foreach (ManagementObject result in results)

   44         {

   45             Items.Add(new DiskInfo

   46             {

   47                 DriveLetter = result[“Name”].ToString(),

   48                 VolumeName = result[“VolumeName”].ToString(),

   49                 Size = (double.Parse(result[“Size”].ToString()) / MEGABYTE),

   50                 Free = (double.Parse(result[“FreeSpace”].ToString()) / MEGABYTE),

   51             });

   52         }

   53     }

   54 }

Legg merke til den SQL-lignende spørringen på linje 34/35.

Nå gjenstod det bare å definere en liste med servere, initialisere en bunch med command-objekter, eksekvere dem, og loope gjennom resultatet for å skrive objektene til konsollet. Det gjorde jeg i Main:

   10 static void Main(string[] args)

   11 {

   12     string[] servers = { “s1dev02″, “s1dev03″, “s1dev04″, “s1dev05″, “s1hvsql01″ };

   13 

   14     try

   15     {

   16         foreach (var server in servers)

   17         {

   18             Console.WriteLine(server + “:”);

   19 

   20             var diskInfoRetriever = new DiskInfoRetriever(server);

   21             diskInfoRetriever.Execute();

   22             diskInfoRetriever.Items.ForEach((disk) => Console.WriteLine(disk));

   23 

   24             Console.WriteLine();

   25         }

   26     }

   27     catch (Exception ex)

   28     {

   29         Console.ForegroundColor = ConsoleColor.Red;

   30         Console.WriteLine(ex.ToString());

   31     }

   32 

   33     Console.WriteLine();

   34     Console.WriteLine(“press ENTER to exit..”);

   35     Console.ReadLine();

   36 }

20 minutter tidligere hadde jeg ingen anelse om at jeg kom til å lage dette her, eller at det var så enkelt. For fremtiden skal jeg tenke meg om en ekstra gang før jeg klager over en manglende, teknisk løsning. Det meste lar seg løse, og mye er enklere enn man tror.

Neste steg blir å lage en ny modul til ContikiCenter som kan informere oss om når disker nærmer seg smertegrensen. Her er forresten resultatet av dagens kjøring:

Disk Watcher console app

Obs, det ser ut som om vi må foreta en liten opprydning på s1dev04. Jaja, det får vente til i morgen.. :)


Min NDC 2009 agenda

Det er alt for mange gode foredrag å velge mellom på Norwegian Developer Conference i år. Men det er et luksusproblem, og jeg har nå gjort så godt jeg kan for å planlegge hva jeg skal få med meg. Ser ikke bort fra at ting kan endre seg raskt når jeg faktisk er der, men her er agendaen min slik jeg ser det akkurat nå:

Dag 1

10:15 Usikker, men ender trolig opp på en av disse to:
Ayende Rahien: Building Multi Tenant Applications
Michael Feathers: Seven Blind Alleys in Software Design
11:30 Michael Feathers: Working Effectively with the Legacy Code: Taming the Wild Code Base
13:30 Ayende Rahien: Producing Production Quality Software
14:45 Udi Dahan: Designing High Performance, Persistent Domain Models
16:00 Phil Haack & Scott Hanselman: The Haacked and Hanselman Show
17:15 Michael Feathers: Design Sense Deep Lessons in Software Design

Det gikk jo ganske greit. Jeg satser på å få med meg både Hanselman og Jeremy D. Miller samme uken på NNUG i Bergen, så jeg dropper deres foredrag (med unntak av The Haacked and Hanselman Show, som sikkert blir fornøyelig).

Dag 2

Dag 2 - 09:00 Ted Neward: Rethinking “Enterprise”
(Litt usikker på om jeg kanskje skulle gått på Peter Provost sitt The Butterfly Effect foredrag.)
10:15 Udi Dahan: Asynchronous Systems Architecture for the Web
(Ayende’s Building Scalable Systems høres også bra ut..)
11:30 Ted Neward: Extend the Customization Possibilities of your .NET App with Script
(Igjen kunne jeg også tenke meg Ayende’s IoC & DI: Breaking out from the Dependency Hell, men scriptable apps er noe jeg er veldig interessert i.)
13:30 Robert C. Martin: Clean Code: Functions
(Alternativt Roy Osherove’s TDD: Using Mock Objects. Men jeg har vært på et par foredrag med ham før..)
14:45 Robert C. Martin: Clean Design: SOLID Principles I and II
16:00 Ted Neward: XML Messaging in .NET using WCF
17:15 Robert C. Martin: Clean Practice: Agility and Craftsmanship

Ok, dag 2 var litt vanskeligere. Jeg velger foredrag som jeg tror vil gjøre meg til en bedre utvikler, og velger bort de som dreier seg om spesifike teknologier. Og så går jeg etter de jeg vet er gode foredragsholdere, som f.eks. Uncle Bob - selv om jeg kjenner temaet hans ganske godt nå.

Dette skulle totalt bli 2 til 3 foredrag med Michael Fethers, 1 til 2 med Ayende, 2 med Udi, 3 med Neward, 3 med Uncle Bob, pluss litt til. Hmm, kanskje burde jeg ha spredd fokuset litt mer, men det får jeg ta på sparket tror jeg.

Dag 3

Full Day Tutorial Scott Bellware: Good Test, Better Code

Dag tre blir forhåpentlig vis en helt enorm opplevelse. Ved hjelp av TDD og BDD vil Bellware lære oss gode design patterns, og ikke minst når man skal bruke dem. Vi vil få praktisk trening i enhetstester, mocking, TDD, BDD, objektorienterte prinsipper, og DSL’er for testing.


Fra enhentstester til kjørbare spesifikasjoner

Dan NorthDet er snart et år siden Dan North var i Bergen og snakket om Behaviour-Driven Development (BDD) - et besøk jeg bl.a. dokumenterte i the Contiki Strip #5. Det var et utrolig inspirerende møte, og Given-When-Then spesifikasjonformatet var noe vi prøvde å jobbe med i tiden etterpå. Men siden vi ikke hadde kommet orntlig igang med å skrive enhetstester, og var langt fra å praktisere TDD, glemte vi det raskt ut, og alt forble ved det gamle.

Nå som jeg har jobbet mer med testdreven utvikling er det på tide å ta dette opp igjen. Jeg vil derfor presentere hvordan man kan gå fra “vanlige” unit-tester til å implementere kjørbare spesifikasjoner (the BDD way), og snakke litt om hvilke fordeler dette gir oss.

Ta f.eks. denne enhetstesten, hentet fra Robert C. Martins bok Agile Principles, Patterns, and Practices in C#:

    1 [Test]

    2 public void DeleteEmployee()

    3 {

    4     int empId = 4;

    5     AddComissionedEmployee t =

    6         new AddComissionedEmployee(empId, “Bill”, “Home”, 2500, 3.2);

    7     t.Execute();

    8 

    9     Employee e = PayrollDatabase.GetEmployee(empId);

   10     Assert.IsNotNull(e);

   11     DeleteEmployeeTransaction dt = new DeleteEmployeeTransaction(empId);

   12     dt.Execute();

   13 

   14     e = PayrollDatabase.GetEmployee(empId);

   15     Assert.IsNull(e);

   16 }

Denne koden har utstract bruk av COMMAND pattern. Testen oppretter først en Employee i databasen (line 5-7), og bekrefter at den ble opprettet (line 9-10). Deretter sender den en slette-komando (linje 11-12) og verifiserer at den nå ikke finnes i databasen lengre (linje 14-15). En helt normal enhetstest etter min mening.

Den uttrykker derimot ikke så mye om intensjonen bak testen - man må lese koden for å forstå hva som skjer, og strukturen er helt og holdent opp til utvikleren. En bedre måte å gjøre dette på er å sette opp et Given-When-Then-senario: Gitt at vi har en kunde, Når vi ber om å få den slettet, finnes den ikke lengre i databasen.

Implementert vha. TinyBDD, et rammeverkt for å lage BDD-spesifikasjoner under utvikling av Gjøran Hansen, forvandles enhetstesten til noe sånn som dette:

    1 [Test]

    2 public void DeleteEmployeeTransaction_should_remove_employee()

    3 {

    4     int empId = 4;

    5 

    6     Scenario.New(“DeleteEmployeeTransaction should remove employee”,

    7         scenario =>

    8     {

    9         scenario.Given(“An Employee with a specific ID”, () =>

   10             new AddComissionedEmployee(empId, “Bill”, “Home”, 2500, 3.2).Execute())

   11 

   12                 .And(“It exists in the repository”, () =>

   13                     Assert.IsNotNull(PayrollDatabase.GetEmployee(empId)));

   14 

   15         scenario.When(“A delete transaction is sent with that ID”, () =>

   16             new DeleteEmployeeTransaction(empId).Execute());

   17 

   18         scenario.Then(“The Employee should no longer exist in repository”, () =>

   19             Assert.IsNull(PayrollDatabase.GetEmployee(empId)));

   20 

   21     }).Execute();       

   22 }

Om du ikke er vandt med lambda-uttrykk i C# er dette sikkert ganske gresk, men gi det en sjanse likevel. I linje 6 opprettes et nytt senarie. Så på linje 9 sier vi at hvis (Given) en bestemt Employee existerer, og (på linje 12) han finnes i databasen, når (When) man da eksekverer en slette-kommando (linje 15), så (Then) vil Employee’en ikke lenger finnes i basen (linje 18).

Lambda-uttrykkene på linje 10, 13, 16 og 19 utfører og/eller tester det vi har spesifisert i kallene til Given, When og Then. Og sammenligner du med den originale testen så ser du at de egentlig gjør akkurat det samme.

Forskjellen er at BDD-spesifikasjonen beskriver/dokumenterer i klartekst hva situasjonen er og hvilken adferd vi er ute etter. Og spesifikasjonen er så nær koden at det ikke vil være noe problem å holde dem og koden synkronisert om den skulle endres. Den er relativt oversiktelig, og følger et fast mønster.

Og den virkelige gevinsten oppdager du når du innser at disse spesifikasjonene kan skrives av eller i sammarbeid med testere, product owners og til og med kunden selv. Spranget fra kundens eller produktets krav til selve koden blir minimal. Utvikleren og kunden kan f.eks. sitte sammen og skrive spesifikasjonen rett inn i en .cs-fil.

Når spesifikasjonene er definert kan man så kjøre dem underveis i prosjektet, og ta ut rapporter som i klartekst beskriver senariene, og hvilke av dem som er ferdig implementert, hvilke som gjenstår, og eventuelt hvilke som feiler (TinyBDD støtter ikke dette enda såvidt meg bekjent).

BDD er mye mer enn det jeg har vist her. For en praktisk introduksjon til et annet BDD rammeverk som heter MSpec anbefaler jeg at du tar en titt på Rob Conery’s screencast om BDD - meget inspirerende. Og for mer informasjon om BDD generelt er behaviour-driven.org en bra ressurs. Det har vært mye forvirring rundt testdreven utvikling, og BDD gir en litt annen vinkling på problemet, og et sterkere fokus på adferd, noe som vil kunne øke kvaliteten på programvaren du produserer.

Knagger: , , ,


Torbjørn Marø

Torbjørn er systemutvikler og et aktivt medlem av .NET-miljøet i Bergen. Dette er hans blog.


Siste kommentarer


Torbjørn: Vi fikk desverre ikke igang noen god rotasjon, så det er no...

Johannes Brodwall: Spennende varianter. Roterte dere par mye, eller endte folk...

Halvard: Sukk, jeg stiller vel opp igjen i år også ......

Johannes Brodwall: Dette hadde vært en veldig god kandidat til en lyntale og/e...

Torbjørn: Paul, it sounds to me like what you're stuck in a negative f...

Paul: From my experience: - If I am NOT very concentrated at work...

Torbjørn: Takk for kommentarene! Her er et par tidligere blogposter me...