Od dłuższego czasu we wszystkich swoich aplikacjach używam własnego frameworka, który w szybki sposób daje mi podstawowe funkcjonalności bez potrzeby pisania ich na nowo za każdym nowym projektem. Ma to bardzo dużą zaletę jeśli tyczy się szybkości tworzenia aplikacji. Napisałem go sam, więc posługiwanie się nim jest dla mnie proste, bo znam go na wylot. Jak coś jest nie tak, otwieram źródła i poprawiam. Jak czegoś brakuje, a widzę, że w wielu aplikacjach pisałem podobnym kod to go tam umieszczam. Funkcjonalności, które są tam umieszczone nie są ze sobą powiązane, bo wszędzie jak tylko się da używam kontenera do rozwiązywania zależności, a dzieje się to już tylko w ostatecznej aplikacji.
Niestety wiązało się to z referencją do biblioteki kontenera. W moim przypadku jest to StructureMap, który stale się rozwija a wersje dosyć często zmieniają. Dodatkowym problemem staje się użycie NServiceBus wraz z builderem StructureMap. Wtedy już horror, bo co nowsza wersja NServiceBus to i inna wersja StructureMap użyta. Wszystkie referencje trzeba poprawiać, itd, itp. A najgorsze jest to, że dotyka to także mojego projektu bazowego, czyli frejmworka.
Nie chcę go za każdym razem zmieniać jak tylko StructureMap pojawi się w nowszej wersji, więc zrobiłem fasadę. Jest to chyba jedyna praktyczna przyczyna wykonania fasady dla kontenera. Wszak kontener jest dosyć bazowym elementem w aplikacji i nie zmienia się go od tak.
Kod fasady wygląda nastepująco:
public sealed class IoC
{
private static IoCResolver Resolver { get; set; }
public static void RegisterResolver(IoCResolver resolver)
{
Resolver = resolver;
}
public static IList GetAllInstances()
{
return Resolver.GetAllInstances();
}
public static T TryGetInstance()
{
return Resolver.TryGetInstance();
}
public static object TryGetInstance(System.Type type)
{
return Resolver.TryGetInstance(type);
}
public static T GetInstance()
{
return Resolver.GetInstance();
}
public static object GetInstance(System.Type type)
{
return Resolver.GetInstance(type);
}
}
Jak zapewne niektórzy zauważyli metody mają te same nazewnictwo co metody w StructureMap. Przyzwyczaiłem się, więc po co kombinować. :)
No to teraz co to jest IResolver. Jest to interfejs pod którym w docelowym projekcie aplikacji kryje się konkretna implementacja dla danego kontenera. Interfejs jest w sumie prosty:
public interface IoCResolver
{
IList GetAllInstances();
T TryGetInstance();
object TryGetInstance(System.Type type);
T GetInstance();
object GetInstance(System.Type type);
}
Te kilka metod pozwala w pełni na posługiwanie się kontenerem. Teraz może przykładowa implementacja dla StructureMap:
public class StructureMapResolver : IoCResolver
{
public System.Collections.Generic.IList GetAllInstances()
{
return ObjectFactory.GetAllInstances();
}
public object GetInstance(System.Type type)
{
return ObjectFactory.GetInstance(type);
}
public T GetInstance()
{
return ObjectFactory.GetInstance();
}
public object TryGetInstance(System.Type type)
{
return ObjectFactory.TryGetInstance(type);
}
public T TryGetInstance()
{
return ObjectFactory.TryGetInstance();
}
}
Banalna, nic dodać, nic ująć. :)
Teraz wystarczy przy starcie aplikacji na samym początku zarejestrować nasz resolver:
IoC.RegisterResolver(new StructureMapResolver());
Takie podejście do sprawy rozwiązało mi problem przy zmianach wersji kontenera. Mogę dzięki temu także użyć każdy inny kontener, ale zostanę przy StructureMap. Jak zostanie udostępniona nowa wersja to zmieniam tylko referencję w projekcie głównym nie dotykając projektów bazowych (frameworka).