Der er nu lagt en ny version af EconomyDeluxe op (kan findes her). Der er sket lidt hist og pist - meget refaktorering og bedre struktur samt implementation af Castle Windsor.
Et par noter om opbygningen af mappestrukturen
- To mapper - ERP og Libs. ERP indeholder nu 6 projekter, Libs indeholder 3dje parts frameworks.
- 6 projekter - Behaviour indeholder mine specifikationer og alt dokumentation. Domain indeholder implementation. Domain.Interfaces indeholder… interfaces. IOC indeholder Castle Windsor implementationen og IOC.Behaviour specifikationerne på samme. EconomyDeluxe mappen kommer til at indeholde WPF-applikationen.
For at køre integrationstestene og iøvrigt lege med data, skal følgende trin følges:
- Installation af en PostGRE sql (jeg kører 8.3, men burde virke på tidligere versioner).
- Ændring af connection-stringen i begge projekter til at pege på en ny tom database.
Tiden er primært gået med at implementere en object-factory, object-repositories samt lege med Castle Windsor og Inversion of Control. Jeg havde lidt frygtet repository. Det viste sig dog at være noget nemmere end først antaget. Indtil videre er der implementeret GetById, GetAll og Save på de to klasser, som umiddelbart kan gemmes.
Jeg har valgt en disconnected model til applikationen indtil videre. Det vil sige at jeg overhovedet ikke udnytter NHibernates mulighed for at anvende Unit of Work. Mine scenarier er simpelthen ikke så vilde, at det giver mening endnu. Det gør så samtidig at jeg er nødt til helt at undgå lazy-loads. (Eftersom sessionen lukkes umiddelbart efter at data er hentet, ‘kender’ NHibernate ikke til dataene længere).
Inversion of Control via Castle Windsor har virkelig vist sig at være nemt og dejligt at anvende. Jeg har valgt ikke at bruge den til object factory, men derimod lave min egen factory. Hovedårsagen er at Windsor er lidt for ‘effektiv’ med udledningen af konkrete klasser for min smag, da den også udleder konkrete klasser for public properties af interface-typer. Dvs. at for klasser med ValueObjects fra andre aggregater bliver udledt til en underlig bastard-værdi i modsætning til en standardværdi på null. Jeg ved godt at jeg kunne tilrette koden til at checke for det - men jeg synes det virker uelegant (desuden tog det mig under 5 minutter at lave factory’en med de få klasser jeg har med at gøre endnu).
Derudover har jeg leget en del med SharpDevelop. Jeg må indrømme at jeg bliver mere og mere fascineret af dens enkelhed og non-bloatedness. Jeg har et par krøller jeg skal have rettet ud med hensyn til at opsætte NUnit og PartCover, men det er småting, som mest er min manglende viden. Samtidig er de lige kommet med build 3437, hvor der er et par rare ting med (Extract Method som den vigtigste).
Senere skal jeg have fundet ud af at få installeret et ordentligt Refactoring modul (CodeRush skulle kunne køre med SD, men skal lige have det checket ordentligt ud) - jeg mangler et par af de rigtig dejlige produktivitetsforbedrende funktioner (auto-indsæt af using-direktiver, auto-import af libs osv.).
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 interfaces og implementation.
For at køre integrationstestene og iøvrigt lege med data, skal følgende trin følges:
- Installation af en PostGRE sql (jeg kører 8.3, men burde virke på tidligere versioner).
- Ændring af connection-stringen i begge projekter til at pege på en ny tom database.
God fordøjelse - og husk Rom blev ikke bygget, men drukket på en dag.
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…), 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 of Control. Egentlig handler det meget om at bryde afhængigheder, ved at bruge DI (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.
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… 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… måske et lidt tænkt scenarie, men alligevel meget kraftfuldt.
Den anden halvdel af min kombo er MVP - Model View Presenter. Her går det ud på at separere design fra logik via DI. Ideen er at man indsætter en Mediator (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.
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.
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.
Okay - det var mange termer på en gang - først den ‘gammeldags’ (lettere modificeret til noget a’la ServiceLocator) implementation af MVP:
…
ISomeView _view = new SomeView();
_view.Show();
…
public class SomeView : Window, ISomeView
{
private ISomePresenter presenter
public SomeView()
{
InitializeComponent();
presenter = PresenterService.GetSomePresenter(this);
}
protected override OnInit()
{
presenter.Init();
}
…
}
public class SomePresenter : ISomePresenter
{
private ISomeView _view;
private ISomeService _service;
public SomePresenter(ISomeView view)
{
_view = view;
_service = ServiceService.GetSomeService();
}
public void Init()
{
do some wiring here…
}
…
}
public class SomeService : ISomeService
{
public SomeService(){}
…
}
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.
Nu til vores nye løsning:
…
WindsorContainer _container = new WindsorContainer();
ISomePresenter _presenter = _container.Resolve<ISomePresenter>();
_presenter.View.Show();
…
public class SomeView : Window, ISomeView
{
public SomeView()
{
InitializeComponent();
}
}
public class SomePresenter : ISomePresenter
{
public SomePresenter(ISomeView view, ISomeService service)
{
Do some wiring
}
}
public class SomeService : ISomeService
{
public SomeService(){}
…
}
Hvis det ikke er mere elegant - så må du kalde mig Herrr YAGNI for al evighed. Ooh - og eftersom vi bruger WPF… så kan designet ligge i ‘løse’ XAML-filer, som vi compiler run-time… 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… performance may suffer. Men det vil tiden vise, når vi får de første implementationer op at køre.