Szukaj na tym blogu

środa, 24 listopada 2010

Przegląd PRISM 4 – część 2

Agenda
• Kontener DI
• Rola kontenera DI w Prism
• Wykorzystanie kontenera DI
• ServiceLocator

Kontener DI
Kontener DI (ang. Dependency Injection) jest to wzorzec projektowy mający na celu usunąć z systemu zależności względem klas i wstrzykiwać obiekty implementujące dany interfejs. Słowno muzycznie: kontener stanowi swego rodzaju mapę, do której rejestrujemy powiązania pomiędzy interfejsem i klasą na zasadzie klucz, wartość. Jeśli potrzebujemy obiekt implementujący dany interfejs to odpytujemy się kontenera wskazując mu odpowiedni klucz (interfej), przez co w zamian otrzymujemy wartość (klasa).
Podejście to uniezależnia naszą aplikację od konkretnej implementacji przez co bardzo łatwo będzie można podmienić mechanizm funkcjonowania np.

public interface ICalendarDataService
{
    void GetAllCalendars(Action<Calendar> result, object state);
}

Interfejs ten posiada deklarację metody zwracającej wszystkie klendarze.

public class AzureCalendarDataService : ICalendarDataService
{
    public void GetAllCalendars(Action<Calendar> result, object state)
    {
        // pobierz kalendarze z SQL Azure
    }
}

public class XMLCalendarDataService : ICalendarDataService
{
    public void GetAllCalendars(Action<Calendar> result, object state)
    {
        // pobierz kalendarze z pliku XML
    }
}

public class EFCalendarDataService : ICalendarDataService
{
    public void GetAllCalendars(Action<Calendar> result, object state)
    {
        // pobierz kalendarze z DB za pomocą Entity Framework
    }
}

Powyższe klasy implementują nasz interfejs pobierając kalendarze z różnych źródeł danych.
Ponieważ aplikacja bazuje na interfejsach, nie musi wiedzieć skąd są pobierane dane.
Podmiana źródła danych polega na rejestracji w kontenerze danych odpowiedniej klasy dla naszego interfejsu ICalendarDataService.
Benefity są interesujące (testowalność, prosta konfiguracja, niezależność etc.)

Rola kontenera DI w Prism
Koncepcją Prisma jest możliwość stworzenia aplikacji składającej się z modułów. Tak na prawdę nie interesuje nas jak dana funkcjonalność będzie zaimplementowana. Ważne by moduł implementował wskazany interfejs.

Prism dostarcza implementację dwóch kontenerów:
- MEF,
- Unity.

By z nich skorzystać, wystarczy by nasz Boostraper podziedziczył po odpowiedniej klasie. I tak dla MEF będzie to MefBootstraper, a dla Unity będzie to UnityBootstraper. Oczywiście jeśli ktoś lubi StructureMap, AutoFac albo inny kontener DI, nie stoi nic na przeszkodzie by napisać własną implementację Boostrapera rejestrującego wasz ulubiony konter.

Wykorzystanie kontenera DI
Najczęściej wykorzystywanymi operacjami przy pracy z kontenerem DI są:
- rejestracja typu,
- pobieranie instancji typu z kontenera,
- rejestracja zadanej instancji (Singleton),
- wstrzykiwanie obiektów do konstruktora

Rejestracja typu w kontenerze Unity
this.Container.RegisterType<ICalendarDataService, XMLCalendarDataService>();

Rejestracja typu w kontenerze MEF
[Export(typeof(ICalendarDataService))]
public class XMLCalendarDataService: ICalendarDataService
{
}

Pobieranie instancji typu z kontenera Unity
var cal = this.Container.Resolve<ICalendarDataService>();

Pobieranie instancji typu z kontenera MEF
var cal = this.Container.GetExportedValue<ICalendarDataService>();

Rejestracja zadanej instancji (Singletone) w kontenerze Unity
container.RegisterInstance<ICalendarDataService>(new AzureCalendarDataService);

Rejestracja zadanej instancji (Singletone) w kontenerze MEF
this.Container.ComposeExportedValue<ICalendarDataService>(this.serwisKalendarza);

Wstrzykiwanie obiektów do konstruktora.
W Unity operacja ta dzieje się automatycznie czyli np.
public class CalendarViewModel
{
    private IEventAggregator _eventAggregator;

    public CalendarViewModel(IEventAggregator eventAggregator)
    {
       _eventAggregator = eventAggregator;
    }
}

Zakończy się zainicjalizowaniem _eventAggregator odpowiednim obiektem zarejestrowanym w kontenerze Unity.

W MEF działa to analogicznie z tym, że nad klasą musimy umieścić atrybut

[ImportingConstructor]
public class CalendarViewModel

Przedstawione przykłady to tylko szybka ściąga. Po szczegóły odsyłam pod adresy:
http://unity.codeplex.com
http://mef.codeplex.com

ServiceLocator
ServiceLocator to poprostu nakładka na nasze kontenery mająca na celu ułatwienie przy próbie zmiany kontenera podczas procesu tworzenia aplikacji.
Jedyną opcją jaką oferuje ServiceLocator jest możliwość pobrania typu obiektu i to w dodatku opakowanego w object przez co musimy to jeszcze rzutować na odpowiedni interfejs.
ServiceLocator.Current.GetInstance<IRegionNavigationService>();

Fajną rzeczą z ServiceLocatorem jest fakt, że jest on klasą statyczną dostępną w całej aplikacji tak więc przydaje się w sytuacjach gdzie potrzebujemy coś pobrać z kontenera, a nie mamy do niego dostępu np. tworzymy nasze ViewModel’e w stylu Blendability, gdzie instancja jest tworzona przez View (View First strategy).

Brak komentarzy:

Prześlij komentarz