Komponentowe i rozproszone Komponenty i zależności
Moduł Obecne rozwiązania pozwalają łatwo dzielić kod na moduły Nie utrudnia to kompilacji, łatwo zmienić rozmieszczenie klas itd. Serwery CI pozwalają wykrywać niespójności pomiędzy modułami Nie ma dużej różnicy czy dostarczamy program w postaci 1 pliku wykonywalnego czy grupy plików. Na potrzeby instalacji można scalić kilka assemblies w jedno np (exe+dll-ki -> exe) Na poziomie kodu i tak dążymy do rozluźnienia zależności między klasami, użycie różnych assemblies jest właściwie “bezbolesne” Rozwiązania typu kontenery DI pozwalają na rozluźnienie zależności między klasami
Komponent Co jeżeli chcemy dostarczać poszczególne moduły oddzielnie? Jak testować komponent, który może być używany w kilku systemach/aplikacjach? Co jeżeli każdy użytkownik chce mieć swoje zmiany ? Co jeżeli zmiana zaindukowana przez jeden system wymusza testowanie kodu ze wszystkimi? Czyli przy każdej zmianie trzeba testować wszystkich użytkowników/ ???
Komponenty vs. moduły Komponent Jest dostarczany niezależnie ma indywidualny cykl życia/testowania itd Poszczególni użytkownicy mogą używać swoich wersji. Mogą się “przesiąść” na nowszą wersję w wybranym, dogodnym momencie (jak robią własne zmiany) Można wersjonować komponenty na poziomie binarnym
.Net CLI pozwala na wykorzystywanie kodu napisanego w dowolnym języku wspierającym .NET Zarządzalne biblioteki w połączeniu z refleksją pozwalają na łatwe użycie kodu na poziomie binarnym (CL) Wersjonowanie pozwala na jednoczesne użycie różnych wersji tej samekj biblioteki Na poziomie systemowym można rejestrować biblioteki w GAC
.Net vs Java …. JNBridge IKVM WebServices ?
Uwaga Komponent ≠ Serwis
.NET Podzespoły (assemblies) Logiczny zbiór jednego lub więcej EXE, DLL Wydzielone pliki z zasobami Manifest Definiowanie podzespołu: AL.EXE (ILDASM.EXE) IDE
.NET Manifest Definiuje obiekty wchodzące w skłąd podzespołu Może określać kulturę (język, formaty, itd) Może zawierać klucze publiczne Może zawierać prywatne atrybuty (ignorowane przez CLR) Może określać wymagany/pożądany CLR
.NET wdrażanie podzespołów Podzespoły mogą być współdzielne i mogą współdzielić pliki Najprostsza postać to pliki w jednym katalogu (nie wymagane żadne wpisy do rejestru) Brak ew. konfliktów Pot. multiplikacja bibliotek Współdzielenie zasobów: GlobalAssemblyCache (GACUTIL.EXE, AssemblyCacheViewer) Wamaga silnych nazw: Nazwa+fragment klucza publicznego SN.exe -> plik.snk -> projekt
.NET Wersjonowanie Alternatywa dla piekła DLLi „Docelowy plik jest nowszy niż kopiowany. Czy kontynuować ?” (9()% - TAK) Manifest zawiera informację o wersji Główny i drugorzędny numer wersji Numer kompilacji Numer korekty Manifest zawiera wersję informacyjną CLR próbuje znaleźć wersje podzespołów wymagane lub dopuszczane przez klienta
.Net: Global Assembly Cache Rejestracja/derejestracja gacutil [options] [assemblyName | assemblyPath | assemblyListFile] Assembly musi być podpisane
Zależności Rodzaje zależności: (Afferent) - kto zależy (Efferent) – od kogo zależy efferent class Y { … } class X { … Y field; } afferent
Podział na komponenty REP: The Reuse/Release Equivalency Principle: The granule of reuse is the granule of release. Only components that are released through a tracking system can be effectively reused. This granule is the package. CRP: Common Reuse Principle:The classes in a package are reused together. if you reuse one of the classes in a package, you reuse them all. CCP: Common Closure Principle:The classes in a package should be closed together against the same kinds of changes. a change that affects a package affects all the classes in that package. Robert C. Martin
Komponent CRP: podział dla uniknięcia zbędnych releasów REP: Grupowanie dla reużycia CCP: Grupowanie dla potrzeb utrzymania Zbędne releasy Zmiany rozproszone między komponentami Niewygodne (re)użycie Kompo-nent Granice podziału determinują częstość releasów, wygodę developmentu i korzystania z komponentu
Zależności między modułami Acyclic Dependencies Principle: the dependency structure between packages must be a directed acyclic graph (dag). that is, there must be no cycles in the dependency structure. Stable Dependancies Principle: the dependencies between packages in a design should be in the direction of the stability of the packages. A package should only depend upon packages that are more stable that it is. Stable Abstractions Principle: packages that are maximally stable should be maximally abstract. instable packages should be concrete. The abstraction of a package should be in proportion to its stability. Robert C. Martin
Miara zależności Niestabilność: I =CE / (CA+ CE) Abstakcyjność: A=NA/N CA (X) = liczba funkcji/klas/modułów, które zależą od (odwołują sie do) X CE (X) = liczba funkcji/klas/modułów do których odwołuje się X N = liczba typów NA = liczba typów abstrakcyjnych Niestabilność: I =CE / (CA+ CE) Abstakcyjność: A=NA/N
Stabilność Moduły stabilne (trudne do zmiany) nie powinny zależeć od niestabilnych (łatwych do zmiany) Co jest łatwe do zmiany? Klasy finalne, do których niewiele kodu się odwołuje. Co jest trudne do zmiany? Klasy finalne, do których odwołuje się dużo kodu, klasy bazowe, interfejsy. Co zmienia się często: klasy konkretne, implementacje logiki biznesowej Co zmienia się rzadko: abstrakcje – interfejsy, klasy abstrakcyjne
Optymalna sytuacja Miara pot. problemów: D = |A+I-1| A Strefa braku użyteczności: klasy abstakcyjne, z których nikt nie korzysta Klasy czysto abstrakcyjne o dużej stabilności 1 I Strefa bólu: klasy konkretne, b. często używane w kodzie Klasy konkretne o małej stabilności Miara pot. problemów: D = |A+I-1|
Wiecej nt. komponentów Robert C. Martin - Clean Design, Components Principles.wmv
Rozszerzalna architektura z MS Kontenery IoC/DI Wiele dostępnych rozwiązań Decoupling, testowanie, scentralizowana rejestracja, MEF Wprowadzony w .Net 4.0 Discovery, obsługa metadanych, opóźniona kreacja
MEF - koncepcja
MEF – Podstawowe pojęcia Export Export dla typu/funkcji/pól Wymuszony typ: Export(typeof(Ixxx)) Nazwany kontrakt: Export("name",typeof(Ixxx)) Export nie jest dziedziczony – można użyć InheritedExport Import Dla typu określonego w kodzie Import określonego typu/kontraktu
MEF – Zalecane praktyki Imort/Eksport Interfejsów zamiast konkretnych typow Stałe jako nazwy kontraktow stale/i kontrakty w oddzielnych assembly