IoC contener i Umbraco
Bardzo
lubię concept Inversion of Controll oraz Dependency Inversion. Ucieszyłem
się na wieść, że IoC Contener będzie dostępny w Umboraco 8 “out of the box”,
gdyż twórcy stwierdzili, że społeczność jest już na tyle dojarzała, że udźwignie
nieco bardziej zaawansowane tworzenie obiektów. ;)
Niestety w wersji 7 Umbraco jeszcze nie mamy IoC Contenera,
ale możemy temu zaradzić instalując samemu któryś z kontenerów. Lubię korzystać
z StructureMap-a i to właśnie na niego padł mój wybór. Już nie pamiętam dokładnie
skąd wziąłem przykład implementacji, ale np. tu jest dostępny opis, jak to
zrobić:
Jest też nowa wersja tej biblioteki, która jest przystosowana
do wersji 4 StructureMap-a i tej właśnie używam w swoim projekcie.
Dzięki temu, możemy korzystać z odwrócenia zależności
(jednej z zasad SOLID-nego programowania) w prosty sposób. Ja używam
StructureMapa do wstrzykiwania zależności w konstruktor klas. Można również za
jego pomocą wstrzykiwać właściwości klas, ale ja nie jestem fanem tego
rozwiązania. Moim zdaniem kod jest bardziej czytelny przy wstrzykiwaniu do
konstruktorów.
Co zyskujemy używając „Dependency Inversion” w naszym kodzie
za pomocą StructureMap-a? Postaram się to pokazać na podstawie mojego projektu J
W swoim kodzie mam coś takiego jak Buildery do sekcji na
stronie. Żeby mieć spójną koncepcję jak wygląda Builder, wydzieliłem abstrakcję
do Interfejsu, który każdy Builder musi implementować:
public interface ISectionBuilder { string ViewName { get; } BaseViewModel CreateViewModel(IPublishedContent content); bool DeosApply(string documentAlias); }
ViewName to Nazwa
widoku (pliku cshtml) znajdującego się w folderze „Partials”, który ma zostać
użyty do zbudowania sekcji.
Metoda DoesApply dostaje w parametrze alias noda aktualnie renderowanego
i ma zwrócić czy ten builder się nada, czy też nie.
Ostatnia metoda jest odpowiedzialna za zbudowanie
ViewModelu. Bardzo zależało mi na tym, by pozostawić ViewModele w postaci jak najczystszej
(docelowo klasa z tylko i wyłącznie
właściwościami)
Dzięki StructureMap mogę zarejestrować wszystkie Buildery w registry:
For().Use (); For ().Use (); For ().Use (); For ().Use (); For ().Use (); For ().Use ();
W następnej kolejności stworzyłem klasę BuildersFactory,
która w konstruktorze ma wstrzykiwaną listę interfejsów ISectionBuilder. Pod
spodem IoC sprawdzi co zostało zarejestrowane jako implementacja
ISectionBuilder i da mi wszystkie te klasy w liście. Dzięki temu, podczas
renderowania danej sekcji Fabryka odpala metody DoesApply dla każdego buildera
i daje wszystkie te, które potrafią obsłużyć dany node lub pierwszy Builder
który się nada (w zależności od wywołanej metody). Sam Builder także posiada
swój interfejs, który jest zarejestrowany podobnie jak SectionBuildery.
Jak do tej pory stworzyłem jeden kontroler, który będzie
obsługiwał standardowe strony z Umbraco. Każdy Node typu Page będzie renderowany
przez ten kontroler:
public class PageController : RenderMvcController { private readonly IPageModelExtender _pageModelExtender; private readonly ISectionsProvider _sectionsProvider; public PageController(ISectionsProvider sectionsProvider, IPageModelExtender pageModelExtender) { _pageModelExtender = pageModelExtender; _sectionsProvider = sectionsProvider; } public override ActionResult Index(RenderModel model) { var allSections = model.Content.Children.ToList(); var listOfSectionsToRender = _sectionsProvider.GetListOfSectionsToRender(allSections); var pageViewModel = new PageViewModel {Sections = listOfSectionsToRender}; return View("Page", _pageModelExtender.ApplyLayoutToModel(pageViewModel, model.Content)); } }
Zasada działania jest dość prosta. Podczas renderowania
danego „Page-a”, brane są wszystkie jego dzieci (Nody będące piętro niżej w
drzewku). Każde dziecko jest renderowane przez SectionBuilder, który potrafi
obsłużyć dany alias. Na samym końcu, gdy mamy już listę ViewModeli oraz nazw
widoków, by wyrenderować wewnątrz strony wszystkie sekcje, dokładane są header
oraz footer wraz z ewentualnymi zmianami w meta tagach, gdy jest taka potrzeba.
Jak widać sama zasada działania jest prosta, czyli taka jak
najbardziej lubię ;) Kolejnym dużym plusem stosowania IoC Contenera i
wstrzykiwania zależności są znaczące ułatwienia przy testach jednostkowych.
Dzięki takim bibliotekom jak Moq, możemy tworzyć „mocki” obiektów czyli takie
wydmuszki, które mają np. zahardkodowaną wartość zwracaną z metod
wstrzykniętych klas niezależnie od logiki w ciele tej metody. Dzięki temu
możemy wyizolować testy, testować tylko i wyłącznie logikę danej klasy, nie
przejmując się że coś pójdzie nie tak w jednej z klas używanej przez testowaną.
Pewnie kiedyś szerzej opiszę koncepcję testów jednostkowych wykorzystujących
zasadę odwróconej zależności w kodzie.
Uff… Jak do tej pory mój najdłuższy post, ale chciałem
wyjaśnić sposób mojej implementacji J
Blog nie jest na bieżąco z tym co aktualnie się dzieje w projekcie, jest trochę
do tyłu. Obecnie nadal pracuję nad frontendem i rozbudową kolejnych bazowych
sekcji w stronie. Mały screen na potwierdzenie, że coś się dzieje:
I to już wszystko na dziś, do następnego razu J
Brak komentarzy:
Prześlij komentarz