<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>Goblincave</title>
	<atom:link href="http://goblincave.net/feed" rel="self" type="application/rss+xml" />
	<link>http://goblincave.net</link>
	<description>En blog om kodning i C# og teknologier dertil.</description>
	<pubDate>Fri, 15 Aug 2008 21:15:37 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6</generator>
	<language>en</language>
			<item>
		<title>BDD: EconomyDeluxe v 0.1b - projekt online</title>
		<link>http://goblincave.net/08/2008/bdd-economydeluxe-projekt-online.php</link>
		<comments>http://goblincave.net/08/2008/bdd-economydeluxe-projekt-online.php#comments</comments>
		<pubDate>Fri, 15 Aug 2008 21:15:00 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[EconomyDeluxe]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=228</guid>
		<description><![CDATA[Som lovet, EconomyDeluxe i en lettere ryddet udgave - du kan finde den her.
Du burde kunne loade projektet i din favorit editor. Et par noter om opbygningen af mappestrukturen

To mapper - src og lib. src indeholder de to projekter, lib indeholder 3dje parts frameworks.
To projekter - Behaviour indeholder mine specifikationer og alt dokumentation. Domain indeholder [...]]]></description>
			<content:encoded><![CDATA[<p>Som lovet, EconomyDeluxe i en lettere ryddet udgave - du kan finde den <a href="http://goblincave.net/EconomyDeluxe.zip">her</a>.</p>
<p>Du burde kunne loade projektet i din favorit editor. Et par noter om opbygningen af mappestrukturen</p>
<ul>
<li>To mapper - src og lib. src indeholder de to projekter, lib indeholder 3dje parts frameworks.</p>
<li>To projekter - Behaviour indeholder mine specifikationer og alt dokumentation. Domain indeholder interfaces og implementation.</li>
</ul>
<p>For at køre integrationstestene og iøvrigt lege med data, skal følgende trin følges:</p>
<ul>
<li>Installation af en PostGRE sql (jeg kører 8.3, men burde virke på tidligere versioner).</li>
<li>Ændring af connection-stringen i begge projekter til at pege på en ny tom database.</li>
</ul>
<p>God fordøjelse - og husk Rom blev ikke bygget, men drukket på en dag.</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/08/2008/bdd-economydeluxe-projekt-online.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>Min favorit pattern-kombo</title>
		<link>http://goblincave.net/08/2008/min-favorit-pattern-kombo-pattern.php</link>
		<comments>http://goblincave.net/08/2008/min-favorit-pattern-kombo-pattern.php#comments</comments>
		<pubDate>Thu, 14 Aug 2008 19:43:30 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[Patterns]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=218</guid>
		<description><![CDATA[Agile processer snakker tit om anvendelsen af patterns. Jeg er ved at have nogenlunde styr på de mest almindelige GoF patterns (hvem hulen bruger Monostate&#8230;), og i dag fandt jeg en meget lækker og elegant kombination, som måske vil ændre min måde at skrive kode for alvor.
De fleste har nok hørt om IoC - Inversion [...]]]></description>
			<content:encoded><![CDATA[<p>Agile processer snakker tit om anvendelsen af patterns. Jeg er ved at have nogenlunde styr på de mest almindelige <a href="http://en.wikipedia.org/wiki/Gang_of_Four_(software)">GoF</a> patterns (hvem hulen bruger Monostate&#8230;), og i dag fandt jeg en meget lækker og elegant kombination, som måske vil ændre min måde at skrive kode for alvor.</p>
<p>De fleste har nok hørt om <a href="http://en.wikipedia.org/wiki/Inversion_of_control">IoC</a> - Inversion of Control. Egentlig handler det meget om at bryde afhængigheder, ved at bruge <a href="http://en.wikipedia.org/wiki/Dependency_injection">DI</a> (Dependency Injection). Dvs. at istedet for at to klasser er direkte afhængige af hinandens implementation - er de alene afhængige af hinandens interface. IoC udmærker sig ved at gøre det muligt først at lave koblingen run-time - og for de noget mere avancerede scenarier give mulighed for at udskifte konkrete implementationer run-time.</p>
<p>Vi bruger IoC via Windsor Castle - et fantastisk Framework, som virkelig gør det nemt at opsætte afhængigheder (kræver at man kan stave rigtigt i sin XML&#8230; men ellers rimelig nem at sætte op). I princippet kan vi på den samme installation køre med forskellige opsætninger for to forskellige brugere&#8230; måske et lidt tænkt scenarie, men alligevel meget kraftfuldt.</p>
<p>Den anden halvdel af min kombo er <a href="http://en.wikipedia.org/wiki/Model_View_Presenter">MVP</a> - Model View Presenter. Her går det ud på at separere design fra logik via DI. Ideen er at man indsætter en <a href="http://en.wikipedia.org/wiki/Mediator_pattern">Mediator</a> (presenter) imellem view og model (typisk en domain service eller direkte på et domæneobjekt). Dvs. at presenteren får kald fra viewet, når brugeren interagerer og formidler det videre til modellen, som så kan lave call-backs osv. View og model kender ikke hinanden - kun presenteren kender begge. MVP kan godt virke lidt uelegant, fordi viewet initierer presenteren med sig selv som argument i constructoren i de fleste implementationer. </p>
<p>Det er muligt at trække afhængighederne fra MVP ud i en noget IoC lignende med en PresenterService.CreateViewPresenter()-noget, men så er vi blot afhængige af en ny klasse. </p>
<p>Nu kommer komboen så. Via IoC udleder vi den konkrete implementation af presenteren direkte og udleder de konkrete implementation af model og view - altsammen run-time.</p>
<p>Okay - det var mange termer på en gang - først den &#8216;gammeldags&#8217; (lettere modificeret til noget a&#8217;la ServiceLocator) implementation af MVP:</p>
<blockquote><p>&#8230;<br />
ISomeView _view = new SomeView();<br />
_view.Show();<br />
&#8230;</p>
<p>public class SomeView : Window, ISomeView<br />
{</p>
<blockquote><p>private ISomePresenter presenter<br />
public SomeView()<br />
{</p>
<blockquote><p>InitializeComponent();<br />
presenter = PresenterService.GetSomePresenter(this);</p></blockquote>
<p>}<br />
protected override OnInit()<br />
{</p>
<blockquote><p>presenter.Init();</p></blockquote>
<p>}</p></blockquote>
<p>&#8230;<br />
}</p></blockquote>
<blockquote><p>public class SomePresenter : ISomePresenter<br />
{</p>
<blockquote><p>private ISomeView _view;<br />
private ISomeService _service;</p>
<p>public SomePresenter(ISomeView view)<br />
{</p>
<blockquote><p>_view = view;<br />
_service = ServiceService.GetSomeService();</p></blockquote>
<p>}<br />
public void Init()<br />
{</p>
<blockquote><p>do some wiring here&#8230;</p></blockquote>
<p>}<br />
&#8230;</p></blockquote>
<p>}</p></blockquote>
<blockquote><p>public class SomeService : ISomeService<br />
{</p>
<blockquote><p>public SomeService(){}<br />
&#8230;</p></blockquote>
<p>}</p></blockquote>
<p>Ingen direkte koblinger - dejligt - men der er mange ekstra klasser i spil. Det er nemt at teste hver af dem med mocking, men det virker uelegant.</p>
<p>Nu til vores nye løsning:</p>
<blockquote><p>&#8230;<br />
WindsorContainer _container = new WindsorContainer();<br />
ISomePresenter _presenter = _container.Resolve&lt;ISomePresenter&gt;();<br />
_presenter.View.Show();<br />
&#8230;</p>
<p>public class SomeView : Window, ISomeView<br />
{</p>
<blockquote><p>public SomeView()<br />
{</p>
<blockquote><p>InitializeComponent();</p></blockquote>
<p>}</p></blockquote>
<p>}</p></blockquote>
<blockquote><p>public class SomePresenter : ISomePresenter<br />
{</p>
<blockquote><p>public SomePresenter(ISomeView view, ISomeService service)<br />
{</p>
<blockquote><p>Do some wiring</p></blockquote>
<p>}</p></blockquote>
<p>}</p></blockquote>
<blockquote><p>public class SomeService : ISomeService<br />
{</p>
<blockquote><p>public SomeService(){}<br />
&#8230;</p></blockquote>
<p>}</p></blockquote>
<p>Hvis det ikke er mere elegant - så må du kalde mig Herrr YAGNI for al evighed. Ooh - og eftersom vi bruger WPF&#8230; så kan designet ligge i &#8216;løse&#8217; XAML-filer, som vi compiler run-time&#8230; og vi kan dermed skifte dem imens applikationen kører. Okay - træerne gror ikke ind i himlen. Der er en pris at betale for den _meget_ løse kobling&#8230; performance may suffer. Men det vil tiden vise, når vi får de første implementationer op at køre.</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/08/2008/min-favorit-pattern-kombo-pattern.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Dokumentation - less is more</title>
		<link>http://goblincave.net/08/2008/bdd-documentation-less-is-more.php</link>
		<comments>http://goblincave.net/08/2008/bdd-documentation-less-is-more.php#comments</comments>
		<pubDate>Wed, 13 Aug 2008 18:42:37 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[EconomyDeluxe]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=214</guid>
		<description><![CDATA[EconomyDeluxe er allerede på nuværende tidspunkt så omfattende, at det begynder at bliver bøvlet at vedligeholde UserStories på min side i WordPress&#8230; så det har jeg tænkt mig at holde op med. I stedet vil jeg jævnligt lægge en kopi af min solution op på siden, så det er muligt at følge lidt med i [...]]]></description>
			<content:encoded><![CDATA[<p>EconomyDeluxe er allerede på nuværende tidspunkt så omfattende, at det begynder at bliver bøvlet at vedligeholde UserStories på min side i WordPress&#8230; så det har jeg tænkt mig at holde op med. I stedet vil jeg jævnligt lægge en kopi af min solution op på siden, så det er muligt at følge lidt med i hvad der sker.</p>
<p>Inline dokumentation er nu engang så meget lettere at vedligeholde, og den er så dejlig tæt ved hånden. Iøvrigt en af de store fordele ved Scrum - det anbefales at holde dokumentationen på et minimum. BDD foreskriver 25-200 siders dokumentation i papirform - og 200 sider er for enorme systemer.</p>
<p>Jeg lægger applikationen op, når jeg lige pusse-nusset lidt om den. På nuværende tidspunkt kan jeg kodemæssigt gemme alle relevante data fra mine aggregater i en PostGRE database via NHibernate. Jeg kan tilføje bilag til en finanskladde, og tilføje kladdelinjer til bilag udover hvad der er nævnt i de forrige indlæg.</p>
<p>I mine næste indlæg vil jeg gå lidt mere i dybden med NHibernate - jeg er ikke 100% tilfreds med Ayende&#8217;s dokumentation. Nogle steder er den meget indforstået og andre steder er den lidt out-of-date (jeg ved det&#8230; 3.5 er stadig i RC, men meget af det har ikke ændret sig synderligt i de seneste versioner).</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/08/2008/bdd-documentation-less-is-more.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Design ændres og ændringerne forventes!</title>
		<link>http://goblincave.net/08/2008/bdd-design-%c3%a6ndres-og-forventes.php</link>
		<comments>http://goblincave.net/08/2008/bdd-design-%c3%a6ndres-og-forventes.php#comments</comments>
		<pubDate>Sat, 09 Aug 2008 21:46:03 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[EconomyDeluxe]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=202</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>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.</p>
<p>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:</p>
<blockquote><p>using System;<br />
using System.Collections;</p>
<p>public interface <strong>IGeneralLedger</strong><br />
{</p>
<blockquote><p>void AddAccount(ILedgerAccount account);<br />
void DeleteAccount(ILedgerAccount account);<br />
IDictionary Accounts{get;}</p></blockquote>
<p>}</p></blockquote>
<p>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:</p>
<blockquote><p>&#8230;<br />
IGeneralLedger _generalLedger = new GeneralLedger();<br />
&#8230;<br />
_generalLedger.Accounts.Clear();<br />
&#8230;
</p></blockquote>
<p>Heldigvis blev problemet hurtigt opdaget af brugeren, så vi kan nå at ændre interfacet inden der er kommet alt for meget funktionalitet i spil:</p>
<blockquote><p>using System;<br />
using System.Collections;</p>
<p>public interface <strong>IGeneralLedger</strong><br />
{</p>
<blockquote><p>void AddAccount(ILedgerAccount account);<br />
void DeleteAccount(ILedgerAccount account);<br />
int AccountCount{get;}<br />
bool ContainsAccount(int accountId);</p></blockquote>
<p>}</p></blockquote>
<p>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 &#8216;lov&#8217; til at bruge fra IDictionary&#8217;et&#8230; Dels giver det sig selv, at det ville blive uoverskueligt, hvis der var flere af den slags konventioner&#8230; og chancen for fejl ville være overhængende.</p>
<p>En rigtig grim og dårlig løsning ville være at lave min egen version af IDictionary, som nedarver fra Dictionary&lt;int,ILedgerAccount&gt;, 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.</p>
<p>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&#8230; YAGNI. </p>
<p>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&#8217;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.</p>
<p>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&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/08/2008/bdd-design-%c3%a6ndres-og-forventes.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Refleksion og værktøjer</title>
		<link>http://goblincave.net/08/2008/bdd-refleksion-og-v%c3%a6rkt%c3%b8jer.php</link>
		<comments>http://goblincave.net/08/2008/bdd-refleksion-og-v%c3%a6rkt%c3%b8jer.php#comments</comments>
		<pubDate>Thu, 07 Aug 2008 19:11:30 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=198</guid>
		<description><![CDATA[Jeg har nu haft en god uges tid til at reflektere og øve mig på BDD. Der er stadig områder, som jeg kun har berørt sporadisk, men der danner sig et billede. Jeg er på nuværende tidspunkt overbevist om at BDD er vejen frem. Selv for små projekter, kan det give mening at arbejde efter [...]]]></description>
			<content:encoded><![CDATA[<p>Jeg har nu haft en god uges tid til at reflektere og øve mig på BDD. Der er stadig områder, som jeg kun har berørt sporadisk, men der danner sig et billede. Jeg er på nuværende tidspunkt overbevist om at BDD er vejen frem. Selv for små projekter, kan det give mening at arbejde efter BDD. Jeg hygger mig stadig med EconomyDeluxe - det er tvivlsomt om det nogensinde når et niveau, hvor jeg vil begynde aktivt at forfølge en frigivelse, men det er stadig hyggeligt at have noget at fokusere sine eksperimenter med. Jeg vil komme med sporadiske opdateringer på, hvor jeg er i processen.</p>
<p>Samtidig har jeg stiftet nærmere bekendtskab med nogle gode værktøjer. Min definition af gode værktøjer er, at de gør mit arbejde lettere. Det kan lyde banalt, men et værktøjs rolle er ikke at ændre på min arbejdsgang eller introducere mig for nye arbejdsgange. De skal lette eller understøtte dem jeg allerede har. Nedenfor vil jeg nævne mine favoritter indenfor forskellige kategorier.</p>
<p>Vinderen for bedste overall test-suite er <a href="http://www.nunit.org">NUnit</a>. Det er ikke uden grund, at langt de fleste TDD&#8217;ere bruger den. Det virker bare - og med tilføjelsen af <a href="http://www.ayende.com/projects/rhino-mocks.aspx">Rhino Mocks</a> er jeg nået rigtig langt. Jeg har stadig ikke nået at få stiftet bekendtskab med White - men jeg vender tilbage, når jeg får det testet. Det er bygget ovenpå Rhino og NUnit - og har sin force inden for testning af UI (og mere specifikt WPF). Jeg har været kort omkring NMock2, men kombinationen af at dens syntax er lidt knudret og tung samt at den ikke er typestærk gør, at Rhino løber med sejren. (Og en irriterende fejl i seneste version (2.0.0.44), som kaster exceptions ved mocks af interfaces med generiske metoder.</p>
<p>Vinderen for bedste utility er Reflector. Jeg har den konstant kørende i baggrunden som en udvidet hukommelse til at huske metodekald og obskure nedarvninger. ReSharper er jeg endnu ikke blevet dus med. Jeg er ikke i tvivl om at det er et fantastisk værktøj, men det kræver tid at vænne sig til og udnytte. Jeg er stadig i irritationsfasen, hvor den har &#8216;overtaget&#8217; mine keyboard short-cuts i VS. Jeg håber på, at vi kan få det som et tema på arbejde med &#8220;de 7 taste-tryk, der forbedrer min arbejdshastighed med 50% &#8482;&#8221;. </p>
<p>Og O/RM&#8230; <a href="http://www.hibernate.org">NHibernate</a>. Jeg har kun skrabet i overfladen af dette omfattende værktøj - men kan indtil videre kun sige&#8230; wow. Jeg har været omkring <a href="http://subsonicproject.com/">SubSonic</a> og LiNQ. I det felt er NHibernate lysår foran. Lækkert, lækkert værktøj. Lidt knudret at sætte op og syntaxen er heller ikke 100% strømlinet, men hvad den taber på syntaxen mere end gør den op for i performance, vedligeholdelse og opsætningsmuligheder.</p>
<p>Nogle hårde, men lærerige måneder, som fundementalt har ændret min holdning og tilgang til kodning. Alt er ikke lyserødt, men fremtiden virker mere overskuelig&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/08/2008/bdd-refleksion-og-v%c3%a6rkt%c3%b8jer.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Implementation og tilbageblik</title>
		<link>http://goblincave.net/07/2008/bdd-implementation-og-tilbageblik.php</link>
		<comments>http://goblincave.net/07/2008/bdd-implementation-og-tilbageblik.php#comments</comments>
		<pubDate>Thu, 24 Jul 2008 11:46:29 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Politik]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=180</guid>
		<description><![CDATA[Nedenfor er vist min første implementation af kode i EconomyDeluxe:
using System;
using Domain.Interfaces;
using System.Collections.Generic;
using System.Collections;
public class GeneralLedger:IGeneralLedger
{
private Dictionary _accounts;
public GeneralLedger()
{
_accounts = new Dictionary();
}
public void AddAccount(ILedgerAccount account)
{
if (account == null)
{
throw new ArgumentNullException(&#8221;Null account supplied&#8221;);
}
if (!account.IsValid)
{
throw new ArgumentException(&#8221;Invalid account&#8221;);
}
if (_accounts.ContainsKey(account.AccountId))
{
throw new ArgumentOutOfRangeException(&#8221;AccountId is already present&#8221;);
}
_accounts.Add(account.AccountId,account);
}
public void DeleteAccount(ILedgerAccount account)
{
if (account == null)
{
throw new ArgumentNullException(&#8221;Null account supplied&#8221;);
}
if (!_accounts.ContainsKey(account.AccountId))
{
throw new ArgumentOutOfRangeException(&#8221;AccountId [...]]]></description>
			<content:encoded><![CDATA[<p>Nedenfor er vist min første implementation af kode i EconomyDeluxe:</p>
<blockquote><p>using System;<br />
using Domain.Interfaces;<br />
using System.Collections.Generic;<br />
using System.Collections;</p>
<p>public class <strong>GeneralLedger</strong>:IGeneralLedger<br />
{</p>
<blockquote><p>private Dictionary<int, ILedgerAccount> _accounts;<br />
public <strong>GeneralLedger</strong>()<br />
{</p>
<blockquote><p>_accounts = new Dictionary<int, ILedgerAccount>();</p></blockquote>
<p>}<br />
public void <strong>AddAccount</strong>(ILedgerAccount account)<br />
{</p>
<blockquote><p>if (account == null)<br />
{</p>
<blockquote><p>throw new ArgumentNullException(&#8221;Null account supplied&#8221;);</p></blockquote>
<p>}<br />
if (!account.IsValid)<br />
{</p>
<blockquote><p>throw new ArgumentException(&#8221;Invalid account&#8221;);</p></blockquote>
<p>}<br />
if (_accounts.ContainsKey(account.AccountId))<br />
{</p>
<blockquote><p>throw new ArgumentOutOfRangeException(&#8221;AccountId is already present&#8221;);</p></blockquote>
<p>}<br />
_accounts.Add(account.AccountId,account);</p></blockquote>
<p>}<br />
public void <strong>DeleteAccount</strong>(ILedgerAccount account)<br />
{</p>
<blockquote><p>if (account == null)<br />
{</p>
<blockquote><p>throw new ArgumentNullException(&#8221;Null account supplied&#8221;);</p></blockquote>
<p>}<br />
if (!_accounts.ContainsKey(account.AccountId))<br />
{</p>
<blockquote><p>throw new ArgumentOutOfRangeException(&#8221;AccountId is not present&#8221;);</p></blockquote>
<p>}<br />
_accounts.Remove(account.AccountId);</p></blockquote>
<p>}<br />
public IDictionary <strong>Accounts</strong><br />
{</p>
<blockquote><p>get{return _accounts;}</p></blockquote>
<p>}</p></blockquote>
<p>}</p></blockquote>
<p>Man kan med rette sige, at mit første eventyr ud i BDD har været forholdsvis simpelt. Det er dog ikke en begrænsning for at kunne se styrken i paradigmet. Jeg har implementeret de første to User Stories i programmet - og brugeren har sagt god for det. Brugeren har noget at vise sin chef (okay, han bliver sikkert ikke voldsomt imponeret, men bare vent til den næste iteration&#8230;) - fix og færdig funktionalitet. Programmet kan det, vi har bedt det om at kunne. Det er en af de ting, jeg er mest begejstret for i BDD - ting opfører sig som man har specificeret. Og hvis de ikke gør, så vil en af den række specifikationstests afsløre det ved næste gennemkørsel af test-suiten.</p>
<p>Koden er stort set selv-dokumenterende. Indtil videre har jeg ikke behøvet skrive kommentarer til koden. Ikke at det er en hindring for at gøre det - jeg bør gøre det senere, men jeg har ikke tænkt mig at trætte dine øjne med det - kodeeksemplerne er lange nok i forvejen. Nu kan man vælge at tro mig eller lade være, men ovenstående kodestump tog mig under 5 minutter at skrive. Der er ikke et gram unødig kode. Der er ingen bloat. Der er lige nøjagtig nok kode til at brugeren er glad, hvilket gør mig glad (skizophreni er nu en underlig ting, at skulle forholde sig til). </p>
<p>Men nogen vil måske sige, at det er totalt overkill, at jeg har brugt over 4 gange så mange linjer på test/dokumentation/UL som på kodelinjer i implementationen. Jeg er ikke enig. Min påstand er, at den x5 faktor med antal linjer stadig er mindre end hvis jeg var startet med at implementere fra starten uden User Stories, fordi jeg så ville have skullet ændre ting midt vejs, slette unødig kode, som virkede som en god ide på det tidspunkt. </p>
<p>Og ikke mindst, jeg slipper for at dokumentere kode, som ville have været mere knudret, fordi jeg uanset mine gode intentioner ville være startet med at løse 8 forskellige funtionalitetskrav på en gang (&#8221;Når nu jeg alligevel er ved at lave properties&#8230; så kan jeg ligeså godt oprette de her tre andre, som jeg sikkert får brug for&#8230; ooh, og alle ved at man skal bruge en IsDirty&#8230;) og det ville have været starten på bloat. Hvis det ikke er et krav fra brugeren - så er det per definition bloat. Brugeren har talt - og han sagde at det vigtigste for ham var at han ville kunne oprette og slette finanskonti. Ville det så ikke være fjollet at begynde at lave et avanceret logging system? Ja, skåret ud i pap, men det er hele essensen i, hvorfor de fleste projekter ender på de evige bit-marker. Det er brugeren der betaler - og jo hurtigere han kan se, at han får det han har bedt om - jo mere villig er han til at betale.</p>
<p>Oh ja, og det bedste af det hele? Jeg er ikke det mindste i tvivl om, at min kode virker, fordi jeg har ikke skrevet noget kode, som ikke er testet. Jeg har rent faktisk en code-coverage på 100%!</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/07/2008/bdd-implementation-og-tilbageblik.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Interfaces og Mocking</title>
		<link>http://goblincave.net/07/2008/bdd-interfaces-og-mocking.php</link>
		<comments>http://goblincave.net/07/2008/bdd-interfaces-og-mocking.php#comments</comments>
		<pubDate>Wed, 23 Jul 2008 21:25:43 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[EconomyDeluxe]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=158</guid>
		<description><![CDATA[Efter at have fastlagt min adfærd, skal jeg have nogle ting på plads for at kunne komme videre med en faktisk implementering. Jeg har altid haft problemer med Interfaces - de har altid virket en smule overflødige, når nu man kan lave nedarvning istedet. For så kan man jo bare have en &#8217;super&#8217;-klasse, som alle [...]]]></description>
			<content:encoded><![CDATA[<p>Efter at have fastlagt min adfærd, skal jeg have nogle ting på plads for at kunne komme videre med en faktisk implementering. Jeg har altid haft problemer med Interfaces - de har altid virket en smule overflødige, når nu man kan lave nedarvning istedet. For så kan man jo bare have en &#8217;super&#8217;-klasse, som alle andre klasser arver fra&#8230; og så kalde implementationens properties og methods og bruge basisklassen som input-parameter. Lad os sige det kort - jeg har fået indsigt som gør, at jeg kan se, hvorfor det ikke er heldigt. Når man en gang er blevet nødt til at lave sin &#8217;super&#8217;-klasse om&#8230; og gøre det stykvist&#8230; så har man ikke lyst til at gøre det igen. At omdøbe en klasse er nemt i Visual Studio&#8230; hvis det er alle referencer, der skal omdøbes&#8230; </p>
<p>Og for så at tage den &#8216;teoretiske&#8217; tilgang - designet bør være uafhængig af den faktiske implementation, fordi man så kan skifte implementationen uden at man skal lave alt det andet om. Jeg har sat teoretisk i situationstegn, fordi det i høj grad også er summen af erfaringer gjort i den virkelige verden, som  ligger til grund for teorierne på dette punkt.</p>
<p>Mit næste problem var efter, jeg stiftede bekendtskab med unit-test. Begreber som black-box, white-box, mocking og stubs er intellektuelt svære at koble sammen, når man ser dem i en kontekst, hvor man er fokuseret på at teste implementationen. Hvornår man anvender den ene i forhold til den anden - hvornår det giver mening at teste en metode særskilt, og hvorfor det overhovedet er interessant, når nu outputtet er resultatet af en string.Format(&#8221;{0}.{1}&#8221;,param1,param2).</p>
<p>Men lad os lade EconomyDeluxe tale som eksempel på, hvorfor mocking og interfaces går hånd i hånd, når adfærdsspecifikationer skal laves. Først skal jeg have lavet nogle interfaces for at kunne lave mine tests, som beskrevet i det forrige indlæg. Det virker rimelig klart at vi gerne vil vide om en FinansKonto er gyldig (IsValid) og den skal have et unikt nummer:</p>
<blockquote><p>using System;</p>
<p>public interface <strong>ILedgerAccount</strong><br />
{</p>
<blockquote><p>int AccountId{get;}<br />
bool IsValid{get;}</p></blockquote>
<p>}</p></blockquote>
<p>Første anke kunne være, hvorfor jeg anvender en int som unik nøgle, og ikke en GUID; og hvorfor jeg ikke adskiller Id&#8217;et fra en kontonummeret, så jeg kunne have mulighed for at bruge en string - og hvad med, når det skal gemmes i en db. Svaret er faktisk rimelig simpelt - jeg ved ikke endnu om det er nødvendigt. <a href="http://en.wikipedia.org/wiki/YAGNI" title="Wikipedia">YAGNI</a> og <a href="http://en.wikipedia.org/wiki/KISS_principle" title="Wikipedia">KISS</a> er to patterns som jeg er meget tilhænger af. Jeg er ved at beskrive adfærden - og hvad FinansBalancen skal vide om FinansKonto, for at kunne lave adfærdsspecifikationen - hvorvidt FinansKonti overhovedet skal gemmes i en database, har jeg slet ikke overvejet på nuværende tidspunkt.</p>
<p>FinansBalance er næste offer - vi ved at vi gerne vil kunne tilføje og slette FinansKonti. Og vi vil gerne kunne tælle hvor mange FinansKonti, der ligger i FinansBalancen - og sidst men ikke mindst vil vi gerne kunne finde ud af om en FinansKonto&#8217;s AccountId allerede er tilføjet - vi prøver:</p>
<blockquote><p>using System;<br />
using System.Collections;</p>
<p>public interface <strong>IGeneralLedger</strong><br />
{</p>
<blockquote><p>void AddAccount(ILedgerAccount account);<br />
void DeleteAccount(ILedgerAccount account);<br />
IDictionary Accounts{get;}</p></blockquote>
<p>}</p></blockquote>
<p>Igen har vi en situation, hvor mine valg måske ikke er umiddelbart gennemskuelige. Det ligger nok i kortene at vi skal have noget liste-agtigt at slå op i og tælle på. Grunden til valget af IDictionary er at det er den mest letvægts standardimplementation, som opfylder mine krav - havde jeg valgt IEnumerable (som er mest letvægts), mangler jeg både Count, Add og Delete - så jeg ville skulle lave disse funktioner særskilt - hvilket ville gøre IEnumerable et dårligt valg. ICollection (som er den næste i rækken af liste-implementationer) ville have givet mig Count - men jeg mangler stadig Add og Delete. Og så når vi til IDictionary eller IList - som har alt den funktionalitet, som jeg skal bruge her og nu. IDictionary vinder fordi det i implementationen  ikke er muligt at tilføje den samme nøgle til samlingen - hvilket er en god sikkerhed i mit tilfælde. Opslag på nøgleværdien er desuden billigt, fordi jeg kan slå op i nøgleværdierne alene og jeg er ikke afhængig af at skulle sammenligne objekter på property-niveau.</p>
<p>Og til sidst grunden til at jeg ikke sætter returtypen til Dictionary<int, IAccount>? Fordi jeg igen ikke ved nok til at vælge en implementation, og specifikationen skal stadig være uafhængig - om jeg anvender HashTable eller SortedList osv. er ligegyldig for specifikationen, jeg skal bare vide at jeg har en Count, en Contains, en Delete og en Add metode til rådighed. Men hov, hvorfor kan jeg så tillade mig at bruge void i metoderne? Hvad nu, hvis jeg senere finder ud af, at fx jeg gerne vil have nøglen tilbage? Hvis det bliver nødvendigt - ændrer jeg retur-typen. Det får ingen betydning for den eksisterende implementation, da det alene er en tilføjelse. I den eksisterende kodebase kan jeg med sindsro ignorere returværdien, fordi den ikke er nødvendig i den del af implementationen.</p>
<p>Lad os se, hvad det giver os, når vi skal udbygge GeneralLedgerBehaviour:</p>
<blockquote><p>using System;<br />
using NUnit.Framework;<br />
using NMock2;</p>
<p>[TestFixture()]<br />
public class <strong>GeneralLedgerBehaviour</strong><br />
{</p>
<blockquote><p>private Mockery _mockery;<br />
private IGeneralLedger _generalLedger;<br />
private ILedgerAccount _ledgerAccount;</p>
<p>[SetUp()]<br />
public void <strong>setupBehaviour()</strong><br />
{</p>
<blockquote><p>_generalLedger = new GeneralLedger();<br />
_mockery = new Mockery();<br />
_ledgerAccount = _mockery.NewMock&lt;ILedgerAccount&gt;();</p></blockquote>
<p>}</p>
<p>#region [UserStory: Adding LedgerAccounts to GeneralLedger]<br />
[Test()]<br />
public void <strong>shouldAddLedgerAccountOnAddWithValidLedgerAccount()</strong><br />
{</p>
<blockquote><p>Expect.Once.On(_ledgerAccount).GetProperty(&#8221;IsValid&#8221;).Will(Return.Value(true));<br />
Expect.AtLeastOnce.On(_ledgerAccount).GetProperty(&#8221;AccountId&#8221;).Will(Return.Value(10));<br />
_generalLedger.AddAccount(_ledgerAccount);<br />
Assert.AreEqual(1,_generalLedger.Accounts.Count);<br />
Assert.IsTrue(_generalLedger.Accounts.Contains(10));</p></blockquote>
<p>}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentException))]<br />
public void <strong>shouldThrowExceptionOnAddWithInvalidLedgerAccount()</strong><br />
{</p>
<blockquote><p>Expect.Once.On(_ledgerAccount).GetProperty(&#8221;IsValid&#8221;).Will(Return.Value(false));<br />
_generalLedger.AddAccount(_ledgerAccount);</p></blockquote>
<p>}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentNullException))]<br />
public void <strong>shouldThrowExceptionOnAddWithNullLedgerAccount()</strong><br />
{</p>
<blockquote><p>_ledgerAccount = null;<br />
_generalLedger.AddAccount(_ledgerAccount);</p></blockquote>
<p>}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentOutOfRangeException))]<br />
public void <strong>shouldThrowExceptionWhenAddingLedgerAccountWithIdenticalId()</strong><br />
{</p>
<blockquote><p>Expect.Exactly(2).On(_ledgerAccount).GetProperty(&#8221;IsValid&#8221;).Will(Return.Value(true));<br />
Expect.AtLeastOnce.On(_ledgerAccount).GetProperty(&#8221;AccountId&#8221;).Will(Return.Value(10));<br />
_generalLedger.AddAccount(_ledgerAccount);<br />
_generalLedger.AddAccount(_ledgerAccount);</p></blockquote>
<p>}<br />
#endregion</p>
<p>#region [UserStory: Deleting LedgerAccounts from GeneralLedger]<br />
[Test()]<br />
public void <strong>shouldDeleteSuppliedExistingLedgerAccount()</strong><br />
{</p>
<blockquote><p>Expect.Once.On(_ledgerAccount).GetProperty(&#8221;IsValid&#8221;).Will(Return.Value(true));<br />
Expect.AtLeastOnce.On(_ledgerAccount).GetProperty(&#8221;AccountId&#8221;).Will(Return.Value(10));<br />
_generalLedger.AddAccount(_ledgerAccount);<br />
_generalLedger.DeleteAccount(_ledgerAccount);<br />
Assert.IsFalse(_generalLedger.Accounts.Contains(_ledgerAccount.AccountId));</p></blockquote>
<p>}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentOutOfRangeException))]<br />
public void <strong>shouldThrowExceptionWhenDeletingNonExistingLedgerAccount()</strong><br />
{</p>
<blockquote><p>Expect.AtLeastOnce.On(_ledgerAccount).GetProperty(&#8221;AccountId&#8221;).Will(Return.Value(10));<br />
_generalLedger.DeleteAccount(_ledgerAccount);</p></blockquote>
<p>}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentNullException))]<br />
public void <strong>shouldThrowExceptionWhenDeletingNullLedgerAccount()</strong><br />
{</p>
<blockquote><p>_ledgerAccount = null;<br />
_generalLedger.DeleteAccount(_ledgerAccount);
</p></blockquote>
<p>}<br />
#endregion
</p></blockquote>
<p>}</p></blockquote>
<p>Meget let og elegant, hvis jeg selv skal sige det. Den eneste forudsætning for at ovenstående vil kompilere er, at jeg laver en klasse som hedder GeneralLedger, som implementerer IGeneralLedger. Hvis jeg på et tidspunkt finder på at lave TheNewAndImprovedGeneralLedger, skal jeg kun skifte en enkelt reference i SetUp-metoden for at teste om den overholder mine specifikationer. I min næste post vil jeg vise en implementation, som overholder min specifikation.</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/07/2008/bdd-interfaces-og-mocking.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: De første User stories og Acceptance Criteria</title>
		<link>http://goblincave.net/07/2008/bdd-de-f%c3%b8rste-user-stories-og-acceptance-criteria.php</link>
		<comments>http://goblincave.net/07/2008/bdd-de-f%c3%b8rste-user-stories-og-acceptance-criteria.php#comments</comments>
		<pubDate>Wed, 23 Jul 2008 10:51:02 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[EconomyDeluxe]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=116</guid>
		<description><![CDATA[I mit EconomyDeluxe projekt er det nu tid til at få styr på hvad der er den vigtigste funktionalitet at få på plads først. Efter nøje gennemgang af de User Stories som jeg har skrevet ned på gule post-it&#8217;s, har jeg valgt følgende User Stories skrevet efter formlen &#8220;Som en&#8230;&#8221; &#8220;Vil jeg gerne&#8230;&#8221; &#8220;Så jeg [...]]]></description>
			<content:encoded><![CDATA[<p>I mit EconomyDeluxe projekt er det nu tid til at få styr på hvad der er den vigtigste funktionalitet at få på plads først. Efter nøje gennemgang af de User Stories som jeg har skrevet ned på gule post-it&#8217;s, har jeg valgt følgende User Stories skrevet efter formlen &#8220;Som en&#8230;&#8221; &#8220;Vil jeg gerne&#8230;&#8221; &#8220;Så jeg opnår&#8230;&#8221; og med tilhørende Acceptance Criteria efter formlen &#8220;Under forudsætning af&#8230;&#8221; &#8220;Når&#8230;&#8221; &#8220;Skal&#8230;&#8221;:</p>
<h4>Oprettelse af FinansKonti i KontoPlan</h4>
<blockquote><p>Som en Bogholder<br />
Vil jeg gerne kunne tilføje en FinansKonto til Kontoplanen<br />
Så jeg opnår mulighed for at lave FinansPosteringer på den og kan følge virksomhedens indtægter og udgifter.</p></blockquote>
<blockquote><p><strong>Accepteres når:</strong><br />
Under forudsætning af en gyldig FinansKonto<br />
som ikke eksisterer i KontoPlanen.<br />
Når jeg tilføjer den til KontoPlanen<br />
Skal antallet af FinansKonti stige med 1<br />
og KontoPlanen skal indeholde FinansKontoen.</p></blockquote>
<blockquote><p>Under forudsætning af en ugyldig, allerede eksisterende eller FinansKonto med værdien null<br />
Når jeg tilføjer den til KontoPlanen<br />
Skal der kastes en Exception.
</p></blockquote>
<h4>Sletning af FinansKonti fra KontoPlan</h4>
<blockquote><p>Som en Bogholder<br />
Vil jeg gerne kunne slette ubrugte FinansKonti fra KontoPlanen<br />
Så jeg opnår en strømlinet KontoPlan og kan fjerne fejloprettede FinansKonti.</p></blockquote>
<blockquote><p><strong>Accepteres når:</strong><br />
Under forudsætning af en gyldig FinansKonto<br />
Når jeg fjerner den fra KontoPlanen<br />
Skal antallet af FinansKonti falde med 1<br />
og KontoPlanen skal ikke længere indeholde FinansKontoen.</p></blockquote>
<blockquote><p>Under forudsætning af en ugyldig eller FinansKonto med værdien null<br />
Når jeg fjerner den til KontoPlanen<br />
Skal der kastes en Exception.
</p></blockquote>
<p>Som et designvalg, har jeg valgt at FinansKonti skal have et unikt nummer. Min erfaring (og jeg har snakket med brugeren&#8230; mig, og han er enig) er at bogholdere ikke rigtig kan holde styr på FinansKonti med samme kontonummer.</p>
<p>De nævnte User Stories er også placeret på en statisk side <a href="http://goblincave.net/economydeluxe-stories">her</a> (på engelsk som vanligt) - hvor de vil blive opdateret og udviklet efterhånden som projektet udvikler sig. Der er selvfølgelig en lang række funktionalitet, jeg har planer om, men de her har den største forretningsværdi for mig nu (de fleste andre afhænger nemlig af dem her). For at påbegynde min iteration (jeg kører efter en tillempet Agil udviklingsmetode som jeg kalder SuperScrumLightXP NuMedExtraKaffe eller SSL NMEK - udtales ssschmæk) er det nu tid til at bore lidt i mine User Stories. Hvad forventer jeg af systemets adfærd - hvornår skal det beskytte mig imod mine egne tåbeligheder, og hvilke krav skal jeg stille til returdata?</p>
<p>Endelig er det tid til lidt kode-gymnastik - vi skal have lavet nogle <span style="text-decoration: line-through;">tests</span> adfærdsspecifikationer. Jeg har valgt at de ovenstående User Stories skal håndteres af GeneralLedger, så i gang med kodeklaskeriet.</p>
<blockquote><p>using System;<br />
using NUnit.Framework;</p>
<p>[TestFixture()]<br />
public class <strong>GeneralLedgerBehaviour</strong><br />
{</p>
<blockquote><p>#region [UserStory: Adding LedgerAccounts to GeneralLedger]<br />
[Test()]<br />
public void <strong>shouldAddLedgerAccountOnAddWithValidLedgerAccount()</strong>{}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentException))]<br />
public void <strong>shouldThrowExceptionOnAddWithInvalidLedgerAccount()</strong>{}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentNullException))]<br />
public void <strong>shouldThrowExceptionOnAddWithNullLedgerAccount()</strong>{}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentOutOfRangeException))]<br />
public void <strong>shouldThrowExceptionWhenAddingLedgerAccountWithIdenticalId()</strong>{}<br />
#endregion</p></blockquote>
<blockquote><p>#region[UserStory: Deleting LedgerAccounts from GeneralLedger]<br />
[Test()]<br />
public void <strong>shouldDeleteSuppliedExistingLedgerAccount()</strong>{}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentOutOfRangeException))]<br />
public void <strong>shouldThrowExceptionWhenDeletingNonExistingLedgerAccount()</strong>{}</p>
<p>[Test()]<br />
[ExpectedException(typeof(ArgumentNullException))]<br />
public void <strong>shouldThrowExceptionWhenDeletingNullLedgerAccount()</strong>{}<br />
#endregion
</p></blockquote>
<p>}</p>
</blockquote>
<p>Det fantastiske (?) er at jeg endnu ikke har skrevet et gram kode i min løsning, men jeg har allerede beskrevet specifikationen til hvordan jeg forventer at mit domain opfører sig. Jeg ville i princippet kunne udlevere denne stump kode og en kop kaffe, og så ville en hvilken som helst kodekarl kunne implementere det. Okay - måske ville det hjælpe lidt, hvis jeg skriver inde i min kode, hvad jeg forventer, men på det tidspunkt, er vi så småt begyndt at snakke om vores løsning - og det reserverer jeg til mit næste indlæg.</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/07/2008/bdd-de-f%c3%b8rste-user-stories-og-acceptance-criteria.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Økonomisystemet, som vil forandre verden som vi kender den</title>
		<link>http://goblincave.net/07/2008/bdd-%c3%b8konomisystemet-som-vil-forandre-verden-som-vi-kender-den.php</link>
		<comments>http://goblincave.net/07/2008/bdd-%c3%b8konomisystemet-som-vil-forandre-verden-som-vi-kender-den.php#comments</comments>
		<pubDate>Tue, 22 Jul 2008 20:15:13 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<category><![CDATA[EconomyDeluxe]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=92</guid>
		<description><![CDATA[For at holde fokus med mine indlæg om BDD, har jeg startet et lille økonomisystemsprojekt, som jeg kalder EconomyDeluxe. Det kan virke som en fuldstændig uoverskuelig opgave - men det er netop den slags projekter, som kan få allermest ud af BDD, fordi vi kan dele projektet op i User Stories (opgaver, som en given [...]]]></description>
			<content:encoded><![CDATA[<p>For at holde fokus med mine indlæg om BDD, har jeg startet et lille økonomisystemsprojekt, som jeg kalder EconomyDeluxe. Det kan virke som en fuldstændig uoverskuelig opgave - men det er netop den slags projekter, som kan få allermest ud af BDD, fordi vi kan dele projektet op i User Stories (opgaver, som en given bruger gerne vil kunne løse med applikationen) og Acceptance Criteria (hvornår løser applikationen lige netop den User Story vi beskæftiger os med i tilstrækkelig grad).</p>
<p>Det første skridt på vejen er at få skabt et Ubiquitous Language, <a title="Wikipedia" href="http://en.wikipedia.org/wiki/Domain_driven_design">UL</a> (ikke-tolkbart sprog, dvs. alle ord har én og kun en betydning i denne kontekst). Her løber vi ind i første hurdle - skal man lave UL på dansk eller engelsk? Der er ikke et entydigt svar, og det ville række for vidt at tage den store diskussion her, så jeg har valgt engelsk. Det kan virke mærkeligt eftersom denne blog er på dansk, men mine læsere bør kunne engelsk til husbehov eftersom de er programmører - og da jeg ikke har en kunde i den anden ende (endnu), er jeg kun en om at skulle være enig om hvad de forskellige termer betyder. Desuden, hvis jeg skulle få den ide på et tidspunkt at oversætte denne artikelrække til engelsk, ville det lette opgaven betydeligt. Som et sidste (og dårligt) argument, kræver min æstetiske sans, at jeg programmerer på engelsk&#8230;</p>
<p>Eksempler på UL-termer for et simpelt økonomisystem kunne være:</p>
<ul>
<li>Bogholder (Accountant). En typisk bruger af applikationen, som sørger for indtastning.</li>
<li>KontoPlan/Balance (GeneralLedger). En liste af FinansKonti, som giver et overblik over summen af FinansPosteringer på alle FinansKonti over BogføringsPeriode.</li>
<li>FinansKonto (LedgerAccount). En gruppering af FinansPosteringer med et fælles formål.</li>
<li>FinansPosteringer (LedgerEntry). En postering, som viser den beløbsmæssige påvirkning en disposition forårsager. Af revisions- og skattemæssige årsager, er FinansPosteringer ikke redigerbare efter de er dannet.</li>
<li>FinansKladde (LedgerDraft). En midlertidig samling af FinansKladdeLinjer, som efter at de er gemt bliver til et antal FinansTransaktioner.</li>
<li>FinansKladdeLinjer (LedgerEntryDraft). En midlertidig representation af en redigerbar FinansPostering.</li>
<li>FinansTransaktion (FinancialTransaction). En samling af posteringer, som til sammen giver en debet/kredit påvirkning på 0 (det dobbelte bogholderis princip).</li>
<li>BogføringsPeriode (BookKeepingPeriods). En arbitrær tidsperiode, der typisk strækker sig over et antal måneder.</li>
</ul>
<p>Som det ses, har jeg gjort en del ud af at gøre sproget så præcist som muligt. Hvis der havde været flere om projektet, ville jeg have uddybet det yderligere og arbejdet meget med at gøre de valgte ord tydelige nok, til at alle var klar over den entydige betydning af termerne. Jeg har samlet mit UL på <a title="Economy Deluxe - UL" href="http://goblincave.net/economydeluxe-ul">en statisk side</a> (linket er også vist under &#8220;Navigation&#8221;), så det er muligt at få et samlet overblik over projektet, som det udvikler sig. Det vil ændre sig efterhånden som projektet skrider frem.</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/07/2008/bdd-%c3%b8konomisystemet-som-vil-forandre-verden-som-vi-kender-den.php/feed</wfw:commentRss>
		</item>
		<item>
		<title>BDD: Revolutionen er startet!</title>
		<link>http://goblincave.net/07/2008/bdd-revolutionen-er-startet.php</link>
		<comments>http://goblincave.net/07/2008/bdd-revolutionen-er-startet.php#comments</comments>
		<pubDate>Tue, 22 Jul 2008 17:50:54 +0000</pubDate>
		<dc:creator>GoblinHero</dc:creator>
		
		<category><![CDATA[Kodning]]></category>

		<category><![CDATA[BDD]]></category>

		<category><![CDATA[C#]]></category>

		<guid isPermaLink="false">http://goblincave.net/?p=75</guid>
		<description><![CDATA[Som skrevet tidligere, skal vi i vores projekt til at omskoles til at anvende Behaviour-Driven Developement (BDD), som dybest set er en sammenkobling af Domain-Driven Design (DDD) og Test-Driven Design (TDD). Jeg har derfor besluttet at dedikere en del af min sommerferie til at sætte mig ind i begreberne - for godt nok havde jeg [...]]]></description>
			<content:encoded><![CDATA[<p>Som skrevet tidligere, skal vi i vores projekt til at omskoles til at anvende Behaviour-Driven Developement (<a title="Wikipedia" href="http://en.wikipedia.org/wiki/Behavior_driven_development">BDD</a>), som dybest set er en sammenkobling af Domain-Driven Design (<a title="Wikipedia" href="http://en.wikipedia.org/wiki/Domain-driven_design">DDD</a>) og Test-Driven Design (<a title="Wikipedia" href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a>). Jeg har derfor besluttet at dedikere en del af min sommerferie til at sætte mig ind i begreberne - for godt nok havde jeg en ide om hvad de forskellige ting er, men jeg ville være helt sikker på, at jeg forstået det korrekt. I de følgende indlæg vil jeg gøre rede for de ting, som jeg er stødt på.</p>
<p>Agil udvikling har en række karakteristika som passer rigtig godt sammen med TDD - egentlig virkede det for mig lidt underligt, at de skulle passe godt sammen, fordi TDD for mig lød som en masse design up-front (BDUF). Jeg har tidligere haft en vis anti-pati mod TDD, fordi jeg bruger meget prototyper - og derfor virkede det som en modsætning at skrive test, før jeg havde fundet ud af hvordan jeg ville lave det. Hvordan kan man skrive tests før man ved, hvordan man ville lave det?</p>
<p>For at starte et sted, startede jeg med Wikipedia&#8217;s side om BDD og læste alle links grundigt igennem - og her skete den første åbenbaring: Glem alt om at kalde noget for test - <strong>vi laver ikke unit-tests for at teste - vi laver unit-tests for at specificere system-adfærden</strong>. Jeg tror den sætning er den vigtigste overhovedet - det var i hvert fald den, der åbnede en prås for mig. Pludselig gav sammenhængen mening - jeg havde siddet og kørt rundt i cirkler med tests af implementationen og ikke adfærden af programmet.</p>
<p>Det viser sig (som flere af grundlæggerne iøvrigt bemærker) at ordvalget kan være kritisk for om man forstår et koncept. Når alt i en test-suite hedder noget med &#8216;test&#8217;, så bliver man fokuseret på test-delen. Man spørger sig selv: &#8220;Hvordan kan jeg sikre mig at så meget af min kode bliver testet som overhovedet muligt?&#8221;, &#8220;Hvornår har jeg nok tests?&#8221; osv. En af forfatterne til Wiki-siden prøvede for eksemplets skyld at udskifte alle ord og sætninger, som indeholdet ordet &#8216;test&#8217; med &#8216;adfærd&#8217;/'burde&#8217; (&#8217;behaviour&#8217;/&#8217;should&#8217;) alt efter sammenhængen. Det gjorde udslaget for mig. Det jeg fremover skal koncentrere mig om er, &#8220;hvad vil jeg have programmet til at kunne?&#8221;, &#8220;Hvorkan bør det opføre sig?&#8221;.</p>
<p>For øvelsens skyld, satte jeg mig ned med et simpelt problemområde og skrev specifikationerne til, hvordan jeg forventede, at systemet skulle opføre sig (dybest set bare et objekt med en liste af en anden type objekter). Eksemplet har virkelig givet mig blod på tanden - og i de næste par indlæg vil jeg prøve at beskrive processen.</p>
]]></content:encoded>
			<wfw:commentRss>http://goblincave.net/07/2008/bdd-revolutionen-er-startet.php/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
