BDD: Design ændres og ændringerne forventes!

Skrevet - Saturday, August 9th, 2008 kl. 23:46 | Kategori - Kodning

EconomyDeluxe har udviklet sig - det har vist sig, at det var upraktisk ikke at kunne lukke applikationen, fordi alt information lå i hukommelsen. Der er derfor blevet oprettet User Stories til det sammen med nogle mindre funktionalitetskrav.

Der har dog allerede nu meldt sig en bug på banen. Et uheldigt medlem af udviklerstaben har misbrugt designet. Det vil sige, han har sådan set blot anvendt interfacet og dermed fundet et enkelt punkt, hvor designet ikke helt holdt vand. Problemet findes i interfacet til IGeneralLedger:

using System;
using System.Collections;

public interface IGeneralLedger
{

void AddAccount(ILedgerAccount account);
void DeleteAccount(ILedgerAccount account);
IDictionary Accounts{get;}

}

Problemet er måske ikke helt indlysende. Det er et forholdsvist enkelt design - og det skal vise sig at udviklerens dovenskab er problemet. For at gøre det enkelt (for ham selv) har han valgt at publicere IDictionary for Accounts. Det er faktisk meget uheldigt, fordi han dermed har publiceret indgange til data, som ikke er omfattet af test - og dermed må betragtes som en bug. For de læsere, som stadig ikke er helt med:


IGeneralLedger _generalLedger = new GeneralLedger();

_generalLedger.Accounts.Clear();

Heldigvis blev problemet hurtigt opdaget af brugeren, så vi kan nå at ændre interfacet inden der er kommet alt for meget funktionalitet i spil:

using System;
using System.Collections;

public interface IGeneralLedger
{

void AddAccount(ILedgerAccount account);
void DeleteAccount(ILedgerAccount account);
int AccountCount{get;}
bool ContainsAccount(int accountId);

}

Som altid er der et par designvalg inde over. Jeg kunne have valgt at indføre en konvention om, hvilke funktioner udviklere måtte have ‘lov’ til at bruge fra IDictionary’et… Dels giver det sig selv, at det ville blive uoverskueligt, hvis der var flere af den slags konventioner… og chancen for fejl ville være overhængende.

En rigtig grim og dårlig løsning ville være at lave min egen version af IDictionary, som nedarver fra Dictionary<int,ILedgerAccount>, override funktionerne, som ville give mig problemer og så kaste exceptions, hvis de blev brugt. I et svagt øjeblik, kunne man godt blive fristet af den løsning, hvis applikationen var i produktion og den samlede kodemasse var stor, men det er et design-smell, der lugter så grimt, at det under alle omstændigheder er en ulykke der venter på at ske.

Alternativt kunne jeg have valgt at lave mit eget wrapper-object omkring noget, der implementerer IDictionary, og så publicere (mappe) de funktioner, som jeg har brug for, og ville gøre det nemt senere at implementere alle de andre metoder jeg (højst sandsynligt) får brug for. Men i den begrundelse, ligger samtidig begrundelsen (pun intended) for hvorfor jeg ikke har valgt det… YAGNI.

Så har jeg valgt at publicere Contains alene med en int-værdi. Jeg kunne have valgt at lave den med en ILedgerAccount og så slå op om den’s Id lå i samlingen. Det har jeg valgt ikke at gøre ud fra flere hensyn: For det første gør det det uklart for andre udviklere, hvad den slår op i og med hvad - og hvad formålet med funktionen er. For det andet kunne Udvikleren (med rette) være bekymret for hvad der sker, hvis der er logisk lighed men ikke referentiel lighed osv. osv. Intentionen med kode skal være nem at læse - også ud fra interfaces.

Her var jeg heldig, at problemet blev opdaget forholdsvis hurtigt - hvis det havde været tæt på release, ville gode dyr have været rådne. Det var under testen af første iterations release, at fejlen blev fanget af en uforsigtig udvikler. Men det viser meget godt styrken ved iterativ udvikling. De fleste fejl bliver fanget imens det stadig er forholdsvis billigt at rette. Her var det fire linjers kode i min test-suite og 8 i min produktionskode. Prøv for tankeeksperimentets skyld at overveje hvor dyrt det ville have været med en fejl i kontoplanskoden i et fuldt ERP-program…

Feed | Trackback |

Post a Comment