niedziela, 27 lipca 2008

Dependency Injection + Autofac + mały przykład

[Wymagania: .NET 3.5]
Załóżmy że mamy zrealizować bardzo proste zadanie: zmienić wszystkim osobom o jakimś wieku ich imię. Więc...

Mapujemy osobę:




class Osoba
{
public int Id { get; set; }
public string Imie { get; set; }
public int Wiek { get; set; }

public override string ToString()
{
return string.Format("{0} {1} {2}", Id, Imie, Wiek);
}
}

Symulujemy działanie Data Access Layer (zwracającego wszystkie osoby)

interface IDal
{
IEnumerable<Osoba> Osoby { get; }
}

class Dal : IDal
{
private IEnumerable<Osoba> osoby = new List<Osoba>()
{
new Osoba() { Id = 1, Imie = "osoba1", Wiek = 23},
new Osoba() { Id = 2, Imie = "osoba2", Wiek = 23},
new Osoba() { Id = 3, Imie = "osoba3", Wiek = 32}
};

public IEnumerable<Osoba> Osoby { get { return osoby; } }
}

Tworzymy Data Access Object (LINQ wyciągający osoby przy użyciu IDal)
interface IDaoOsoby
{
IEnumerable<Osoba> FindByWiek(int wiek);
}

class DaoOsoby : IDaoOsoby
{
private IDal dal;

public DaoOsoby(IDal dal)
{
this.dal = dal;
}

public IEnumerable<Osoba> FindByWiek(int wiek)
{
return from osoba in dal.Osoby
where osoba.Wiek == wiek
select new Osoba() { Id = osoba.Id, Imie = osoba.Imie, Wiek = osoba.Wiek };
}
}


Serwis odpowiedzialny za zmianie imion (główna logika)
interface IServiceOsoby
{
void ZmienImie(int wiek);
}

class ServiceOsoby : IServiceOsoby
{
private IDaoOsoby daoOsoby;

public ServiceOsoby(IDaoOsoby daoOsoby)
{
this.daoOsoby = daoOsoby;
}

public void ZmienImie(int wiek)
{
IEnumerable<Osoba> osoby = daoOsoby.FindByWiek(wiek);

foreach (var osoba in osoby)
{
osoba.Imie = "zmienione imie";
}
}
}

Dzięki temu, że wszystko oparte zostało na interface'ach uzyskaliśmy bardzo elastyczną
i niepowiązaną żadnymi zależnościami strukturę
. Świetnie, o to właśnie nam chodziło :)
Teraz wystarczy określić zachowanie przez "wstrzykniecie" konkretnych klas do konstruktorów.

Jeśli chodzi o testy jednostkowe, to stworzenie ich nie będzie żadnym problemem.
Dzięki użyciu Dependency Injection możemy testować jedną warstwę niezależnie od drugiej.
Tutaj przyjdą nam z pomocą Mock'i, lecz zostawmy sobie to na później :)

Rzućmy okiem w jaki sposób możemy się tym wszystkim posługiwać.


class Program
{
static void Main(string[] args)
{
// "Normalne" użycie
IDal dal = new Dal();
IDaoOsoby daoOsoby = new DaoOsoby(dal);
IServiceOsoby service = new ServiceOsoby(daoOsoby);

service.ZmienImie(23);

// Autofac
ContainerBuilder cb = new ContainerBuilder();

cb.Register<Dal>().As<IDal>().SingletonScoped();
cb.Register(v => new DaoOsoby(v.Resolve<IDal>())).As<IDaoOsoby>();
cb.Register(v => new ServiceOsoby(v.Resolve<IDaoOsoby>())).As<IServiceOsoby>();

Container container = new Container();
cb.Build(container);

container.Resolve<IServiceOsoby>().ZmienImie(23);
}
}




Jest to tylko bardzo "drobny i ogólny" przedsmak tego co będzie działo w kolejnych postach :)

kod źródłowy

2 komentarze:

Unknown pisze...

odnośnie wzorców projektowych to znalazłem zestaw fajnych przykladow i mysle ze warto opublikowac tego lina na Twoim blogu http://www.go4expert.com/forums/showthread.php?t=5127

Anonimowy pisze...

no nie wygląda to za ciekawie:

cb.Register(v => new ServiceOsoby(v.Resolve())).As();