Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Programowanie komponentowe jesień-zima 2013
Wykład 3 Metody wstrzykiwania zależności dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki
2
Rozwiązywanie zależności komponentów
Metoda tradycyjna polega na aktywnym wyszukiwaniu instancji komponentów w rejestrze kontenera. Metoda nowoczesna wstrzykiwanie zależności (ang. dependency injection). Sposoby wstrzykiwania zależności Interface injection - wstrzykiwanie przez interfejs. Komponenty implementują dedykowany interfejs, poprzez który otrzymują obiekt służący do wyszukiwania zależności Constructor injection wstrzykiwanie przez konstruktor. Wszystkie zależności komponentu muszą zostać spełnione w momencie jego tworzenia, a ich rozwiązaniem i spełnieniem zajmuje się kontener. Setter injection wstrzykiwanie przez właściwości obiektu, czyli przekazanie przez kontener referencji do zależnego komponentu przez metody typu setXXX()
3
Wstrzykiwanie zależności przez interfejs
Obiekt wyszukuje zależności poprzez obiekt kontekstu, przekazany jako parametr metody zdefiniowanej w interfejsie Jest to najpopularniejsza metoda rozwiązywania zależności (np. w J2EE mechanizm JNDI lub Naming Service w CORBA). Metoda polega na implementacji w komponencie interfejsu, stanowiącego znacznik wskazujący, że komponent ten wymaga pewnych zależności. Interfejs deklaruje metodę posiadającą parametr, który jest obiektem kontekstu – wyszukiwarką innych obiektów. Komponent w momencie utworzenia otrzymuje od kontenera instancję kontekstu, którą może zapamiętać, a następnie wykorzystać do rozwiązania swoich zależności. Wady: silne powiązanie z konkretnym kontenerem, jakie wynika z konieczności implementacji przez komponent. Gdy dostępny jest jedynie komponent w wersji binarnej – użycie tego sposobu jest niemożliwe. Zalety: • odsunięcie w czasie rozwiązywania zależności • obsługa zależności cyklicznych • łatwe testowanie
4
Wstrzykiwanie przez interfejs
public interface Serviceable { public void service(ServiceManager manager); } public class Samochod implements Serviceable { private ServiceManager manager = null; private Silnik silnik = null; public void service(ServiceManager manager) { this.manager = manager; this.silnik = (Silnik) manager.lookup("Silnik"); Klasa Samochód deklaruje implementację interfejsu Serviceable. Interfejs ten definiuje metodę service(), przyjmującą parametr typu ServiceManager pełniący rolę obiektu-kontekstu Parametr ten zostanie przekazany komponentowi przez kontener w momencie jego tworzenia poprzez wywołanie metody service(). Obiekt ServiceManager jest wyszukiwarką instancji komponentów w rejestrze kontenera poprzez metodę lookup(), która wyszukuje w rejestrze komponent po nazwie. Komponent Samochód może uzyskać referencję do zależnego komponentu Silnik. WADA: Komponent musi wywołać lookup sam. Rola kontenera ogranicza się do utrzymywania rejestru komponentów i dostarczenia obiektu kontekstu
5
Wstrzykiwanie zależności przez konstruktor
Obiekt otrzymuje wszystkie zależności w postaci parametrów konstruktora Komponent nie musi sam zdobywać zasobów. Komponent deklaruje potrzebę ich wykorzystania w sposób, który jest czytelny dla kontenera i pozwala mu rozwiązać zależności. Odpowiedzialność za dostarczenie zależności jest przeniesiona na kontener. Deklaracja zależności odbywa się poprzez parametry konstruktora Kontener dostarcza obiekty poprzez implementacje tych parametrów Jeżeli korzystając z zarejestrowanych komponentów nie można tego uczynić, utworzenie komponentu zależnego nie jest możliwe i zgłaszane w postaci wyjątku. W przypadku gdy komponent posiada wiele konstruktorów, kontenery próbują utworzyć go korzystając z konstruktora najbardziej specyficznego (tj. o najdłuższej liczbie parametrów), którego zależności mogą rozwiązać.
6
Wstrzykiwanie zależności przez konstruktor
ZALETA: Zapewnienie spójnego stanu komponentu w każdym momencie jego istnienia (co jest jednym z warunków tzw. dobrego obywatelstwa), także bezpośrednio po wywołaniu konstruktora przez komponent. WADA: brak możliwości zrealizowania zależności cyklicznych, tj. takich, w których komponenty zależą od siebie wzajemnie. PRZYKŁAD: W kodzie programu mechanizm ten wymaga jedynie stworzenia w klasie komponentu Samochód konstruktora przyjmującego obiekt o interfejsie Silnik jako parametr. Kontener, tworząc instancję klasy Samochód, musi najpierw utworzyć obiekt typu Silnik, aby przekazać go do właściwego konstruktora. public class Samochod { private Silnik silnik = null; public Samochod(Silnik silnik) this.silnik = silnik; }
7
Wstrzykiwanie zależności przez właściwości
Komponent jedynie deklaruje zależności, spełnieniem zajmuje się kontener Do przekazania instancji obiektów zależnych używamy metod, tzw setterów. Zalety • odsunięcie w czasie rozwiązywania zależności (komponent można skonfigurować dopiero przed samym użyciem) • możliwość obsługi zależności cyklicznych (nie było to możliwe w poprzedniej metodzie) • łatwe testowanie Wada: • stan niespójny po utworzeniu – obiekt nie jest w pełni zainicjowany. Wprawdzie proces ten zwykle jest realizowany przez kontener, który zapewnia poprawność komponentu w momencie jego wykorzystania, jednak w szczególnych okolicznościach może to spowodować nieprawidłowe zachowanie programu.
8
Wstrzykiwanie zależności przez właściwości
public class Samochod { private Silnik silnik = null; public Samochod() { } public void setSilnik(Silnik silnik) { this.silnik = silnik; Klasa Samochód posiada bezparametrowy konstruktor oraz metodę setSilnik(), która przyjmuje obiekt typu Silnik jako argument. Kontener wywoła tę metodę na komponencie, pozwalając mu zapamiętać referencję do przekazanego w postaci parametru obiektu.
9
Konfiguracja rejestru kontenera w Spring
W Java Spring komponenty konfigurujemy w pliku applicationContext.xml <beans> <!-- wstrzykiwanie przez konstruktor --> <bean id="samochod1" class="lab.Samochod"> <constructor-arg> <ref bean="silnik"/> </constructor-arg> </bean> <!-- wstrzykiwanie przez właściwość --> <bean id="samochod2" class="lab.Samochod"> <property> </property> <bean id="silnik" class="lab.Silnik1_6"/> </beans> Plik definiuje dwa komponenty typu Samochód. Komponent dostępny pod nazwą samochod1 wykorzystuje wstrzyknięcie komponentu Silnik przez konstruktor, natomiast pod nazwą samochod2 znajduje się komponent korzystający z wstrzykiwania przez właściwość o nazwie silnik.
10
Programowanie w C# - część 2
11
Zmienne w C# Non-nullable value type
Wartość zdefiniowanego typu, np. int, float Nullable value type Wartość zdefiniowanego obiektu oraz null, np.: Nullable<Int32> oznacza liczby typu Int32 (4 bajty ze znakiem) oraz null object null, referencja do obiektu dowolnego typu, referencja do boxowanej wartości dowolnego typu Klasa Null, referencja do instancji klasy, referencja do instancji klasy dziedziczącej Interfejs Null, referencja do instancji klasy implementującej interfejs, referencja do boxowanej wartości typu implementującego interfejs Tablica Null, referencja do instancji tablicy danego lub kompatybilnego typu Delegat Null, referencja do instancji typu delegata
12
Typy referencyjne i wartościowe
Dla typów referencyjnych przydzielane są obszary pamięci na zarządzanej stercie, typy wartościowe są inicjalizowane zerami. Zwalnianie pamięci – pamięć na stosie jest zwalniania w momencie, w którym dana zmienna opuszcza bieżący zakres przetwarzania. Zwalnianie pamięci na stercie – odpowiada proces odzyskiwania pamięci (sprawdzany jest licznik referencji) 123 i s "Hello world" int i = 123; string s = "Hello world"; Zmienne wartości - bezpośrednio zawierają dane Każda zmienna typu wartości reprezentuje oddzielny obszar pamięci, dlatego operacja na jednej zmiennej nie wpływa na drugą. Zmienne lokalne przechowywane w obszarze pamięci zwanym stosem. Z miejscem przechowywania i sposobem zarządzania tym obszarem związany jest czas życia zmiennej. Czas życia zmiennej możemy zdefiniować jako czas, przez który jest zarezerwowana pamięć dla zmiennej. Pamięć jest zarezerwowana dla zmiennej lokalnej od miejsca jej zadeklarowania do końca bloku, w którym została zadeklarowana. Przed użyciem zmienna musi być zainicjalizowana - musi mieć nadaną wartość.
13
Typy nullable Dla wszystkich typów wartości wprowadzono pewną formę typu (nullable types), której zmienne mogą przyjmować wartości null (wartość nieokreślona). Typy nullable wprowadzono, aby ułatwić współpracę z bazami danych. class NullableExample { static void Main() { int? num = null; if (num.HasValue) System.Console.WriteLine("num = " + num.Value); else System.Console.WriteLine("num = Null"); int y = num.GetValueOrDefault(); try { y = num.Value; } catch (System.InvalidOperationException e){ System.Console.WriteLine(e.Message);
14
Boxowanie 123 i int i = 123; object o = i; int j = (int)o; 123 o System.Int32 123 j W języku C# istnieje jeszcze standardowa konwersja - opakowanie (boxing). Opakowanie wartości polega na niejawnym przekształceniu typu wartości w typ object. Rozpakowanie (unboxing) musi być jawne i może powodować wyjątek. int i = 123; object o = i; // implicit boxing try { int j = (short)o; // attempt to unbox System.Console.WriteLine("Unboxing OK."); } catch (System.InvalidCastException e) { System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
15
Typ wbudowany string Łańcuchy :
public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable{ public char this[int index] {get;} public int Length {get;} public static int Compare(string strA, string strB); public static int CompareOrdinal(string strA, string strB); public static string Format(string format, object arg0); public int IndexOf(string); public int IndexOfAny(char[] anyOf); public int LastIndexOf(string value); public string PadLeft(int width, char c); public string[] Split(params char[] separator); public string Substring(int startIndex, int length); ... } Łańcuchy : łańcuch jest sekwencją wartości typu char porównywane są według wartości niezmienne (ang. immutable) – zmodyfikowane są zapisywane pod innymi adresami niż oryginalne string s = "Hi there."; Console.WriteLine( "{0}", s.ToUpper() ); // Print uppercase copy Console.WriteLine( "{0}", s ); // String is unchanged
16
Przykłady testowania stringów
string s1 = "Hello!"; string s2 = "Yo!"; Console.WriteLine("s1 = {0}", s1); Console.WriteLine("s2 = {0}", s2); Console.WriteLine(); // UWAGA == porównuje referencje a nie obiekty! Console.WriteLine("s1 == s2: {0}", s1 == s2); Console.WriteLine("s1 == Hello!: {0}", s1 == "Hello!"); Console.WriteLine("s1.Equals(s2): {0}", s1.Equals(s2)); Console.WriteLine("Yo.Equals(s2): {0}", "Yo!".Equals(s2));
17
Podstawowe operacje na łańcuchach
Metody Opis Compare() porównanie dwóch łańcuchów Equals() Czy łańcuchu mają tę samą zawartość Concat() utworzenie nowego łańcucha z jednego lub większej liczby Copy() utworzenie nowego łańcucha przez przekopiowanie zawartości Chars[] indekser łańcucha Insert() zwraca nowy łańcuch z dostawionym nowym łańcuchem Remove() usunięcie z łańcucha określonej liczby znaków Split() rozbicie łańcucha na podłańcuchy przy założonym zbiorze ograniczników StartsWith() wskazuje czy łańcuch rozpoczyna się od określonych znaków Substring() wyłuskanie podłańcucha ToLower() zwraca kopię łańcucha składającego się z małych liter Trim() wyrzucenie określonego zbioru znaków z początku i końca łańcucha
18
Znaki specjalne w łańcuchach
Łańcuchy standardowe i dosłowne (ang. verbatim) string rst1 = "Hi there!"; string vst1 there!"; string rst2 = "It started, \"Four score and seven...\""; string vst2 started, ""Four score and seven..."""; string rst3 = "Value 1 \t 5, Val2 \t 10"; string vst3 1 \t 5, Val2 \t 10"; string rst4 = "C:\\Program Files\\Microsoft\\"; string vst4 Files\Microsoft\"; string rst5 = " Print \x000A Multiple \u000A Lines"; string vst5 Print Multiple Lines";
19
Łańcuchy zmienne - StringBuilder
StringBuilder myBuffer = new StringBuilder ("Ala ma kota"); myBuffer.Append(" a Ola psa."); myBuffer.Insert(11, ','); string Ready = myBuffer.ToString(); // Ala ma kota, a Ola psa. Klasa System.Text.StringBuilder konstruowanie i przetwarzanie łańcuchów składowanych w buforze bardziej efektywne w przetwarzaniu łańcuchów public sealed class StringBuilder { public int Capacity {get; set;} public int Length {get; set;} StringBuilder Append(...); StringBuilder AppendFormat(...); StringBuilder Insert(int index, ...); StringBuilder Remove(int startInd, int len); StringBuilder Replace(char oldCh, char newCh); string ToString(); }
20
Typ wyliczeniowy – System.Enum
Typy zawierające zbiór nazwanych stałych Łatwiejsze czytanie kodu – przypisanie łatwo identyfikowalnych nazw do wartości; Łatwość pisania kodu – IntelliSense podpowiada listę możliwych wartości; Łatwość utrzymania kodu - pozwala zdefiniować zbiór stałych i zadeklarować zmienne, które będą akceptować wartości tylko z tego zbioru Definiuje się go przy pomocy słowa kluczowego enum, po którym występuje nazwa nowego typu, a następnie w nawiasach klamrowych podajemy nazwy symboliczne dopuszczalnych wartości, oddzielone przecinkami. Można również przypisać w sposób jawny wartości poszczególnym elementom np.: enum Kolory{czerwony=4, zielony, niebieski=7}; Element zielony ma w sposób niejawny przypisaną wartość 5. Przy definicji typu wyliczeniowego można podać, jaki typ całkowity jest typem bazowym dla definiowanego typu wyliczeniowego. Typem bazowym może być dowolny tym całkowity poza typem char. enum PoryRoku : byte {Wiosna, Lato, Jesien, Zima};
21
System.Enum – przykład enum Color { Red, Green, Blue };
Color a = Color.Red; Color b = Color.Green; Color c = Color.Blue; Console.WriteLine("Wartości typu Color: "); foreach(string s in Enum.GetNames(typeof(Color))) Console.WriteLine(s); Console.WriteLine("Blue należy do typu Color ?: {0}", Enum.IsDefined(typeof(Color),"Blue")); Console.WriteLine("Yellow należy do typu Color ?: {0}", Enum.IsDefined(typeof(Color), "Yellow"));
22
Tablice w C# Cechy tablicy:
jednorodność - jest złożona z elementów tego samego typu zwanego typem podstawowym tablicy swobodny dostęp - wszystkie elementy tablicy są jednakowo dostępne - w dowolnej kolejności i w jednakowym czasie stała (niezmienna) ilość elementów ciągłość - zajmuje ciągły obszar pamięci Pojedynczy element dostępny za pomocą indeksu typ [] nazwa; // Deklaracja zmiennej tablicowej nazwa = new typ[liczba_Elementow]; //utworz. obiektu tablicowego typ2 [] nazwa2 = new typ2[ilosc_Elementow2]; //deklar. z definicją int[] tab1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
23
Wbudowane operacje na tablicach
Kopiowanie wartości elementów jednej tablicy do drugiej tablicy. int[] tab1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int[] tab2 = {11,12,13,14,15,16,17,18,19 }; Array.Copy(tab1,2,tab2,4,4); tab2: {11, 12, 13, 14, 3, 4, 5, 6, 19,} Skopiowanie wartości jednej zmiennej tablicowej do drugiej zmiennej tablicowej, spowoduje że obie zmienne tablicowe będą odwoływać się do tego samego obiektu tablicowego. Sortowanie tablicy int[] tab1 = { 4, 1, 83, 41, 53, 36, 47, 18}; Array.Sort(tab1); tab1: {1, 4, 18, 36, 41, 47, 53, 83,} Inne operacje: Reverse – odwracanie kolejności tablicy lub jej fragmentu Exists, FindLast, FindAll, FindIndex, FindLastIndex – przeszukiwanie tablicy lub jej fragmentu
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.