Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych Instytut Informatyki Rok akademicki 2012/2013 PRACA DYPLOMOWA INŻYNIERSKA Anna Stępień DCI Data Context Interaction ocena przydatności Opiekun pracy: dr inż. Ilona Bluemke Ocena...... Podpis Przewodniczacego Komisji Egzaminu Dyplomowego
Kierunek: Informatyka Specjalność: Inżynieria Systemów Informatycznych Data urodzenia: 1990.02.09 Data rozpoczęcia studiów: 2009.10.01 Życiorys Urodziłam się 9 lutego 1990 roku w Kozienicach. Ukończyłam Szkołę Podstawowa nr 3, Gimnazjum nr 2, a następnie Liceum Ogólnokształcace w Zespole Szkół nr 1 im. Legionów Polskich w Kozienicach. W liceum uczęszczałam do klasy o rozszerzonym programie matematyki i fizyki. W październiku 2009 roku rozpoczęłam studia na Wydziale Elektroniki i Technik Informacyjnych Politechniki Warszawskiej na kierunku Informatyka.... podpis studenta Egzamin dyplomowy Złożył egzamin dyplomowy w dniu... z wynikiem... Ogólny wynik studiów:... Dodatkowe wnioski i uwagi Komisji:.........
Streszczenie Niniejsza praca poświęcona jest ocenie i analizie przydatności wzorca projektowego DCI (ang. Data, Context, Interaction). Przedstawiona została podstawowa terminologia oraz najważniejsze elementy i założenia architektury wzorca. Zasadnicza częścia pracy jest praktyczne wykorzystanie analizowanej techniki. W tym celu przedstawiony został szczegółowy proces projektowania oraz późniejszej implementacji i testowania przykładowej aplikacji wykorzystujacej założenia paradygmatu DCI. Słowa kluczowe: DCI, Data Context Interaction, wzorce projektowe, programowanie obiektowe DCI Data Context Interaction usefulness evaluation The following thesis focuses on the evaluation of the DCI (Data, Context, Interaction) design pattern. The fundamental part of this paper is the presentation of the practical usage of DCI. Particular attention has been paid to the development process of an exemplary application built with DCI principles. This part presents the detailed description of design, implementation and testing phases. On the basis of the gathered experience, the usefulness of DCI in software engineering has been discussed. Key words: DCI, Data Context Interaction, design patterns, object oriented programming
Spis treści 1. Wstęp............................................. 1 2. Wzorzec projektowy DCI................................. 2 2.1. Założenia........................................ 2 2.2. Architektura...................................... 3 2.3. Działanie systemu opartego na DCI......................... 5 2.3.1. Przykład.................................... 5 2.3.2. Wymagania implementacyjne........................ 8 2.3.3. Ograniczenia.................................. 8 2.4. DCI a inne techniki programistyczne........................ 8 2.4.1. MVC....................................... 9 2.4.2. Delegacja.................................... 9 2.5. Założenia stawiane przed DCI............................ 10 3. Projekt i implementacja aplikacji DCI-Project.................... 11 3.1. Wymagania funkcjonalne.............................. 11 3.2. Wymagania niefunkcjonalne............................. 13 3.3. Modelowanie systemu opartego o DCI....................... 13 3.3.1. Diagramy przypadków użycia........................ 13 3.3.2. Model dziedziny................................ 16 3.3.3. Klasy kontekstu i role............................ 17 3.4. Decyzje projektowe.................................. 17 3.5. Implementacja..................................... 20 3.5.1. Architektura aplikacji............................. 20 3.5.2. Moduł DCI................................... 21 3.5.3. Dane...................................... 24 3.5.4. Kontekst, role i interakcje.......................... 24 3.6. Testy aplikacji..................................... 27 4. Analiza DCI......................................... 29 4.1. Wnioski na podstawie aplikacji DCI-Project.................... 29 4.2. Problemy........................................ 33 4.3. Możliwość praktycznego zastosowania....................... 38 5. Podsumowanie....................................... 40 Bibliografia........................................... 41
1. Wstęp Inżynieria oprogramowania wyróżnia wiele wzorców projektowych przedstawiajacych rozwiazania często powtarzajacych się problemów. Wzorce te, na przestrzeni lat na stałe wpisały się w kanony programowania obiektowego. Niniejsza praca poświęcona jest wzorcowi projektowemu DCI (ang. Data, Context, Interaction) [1] i możliwości jego zastosowania w procesie projektowania oraz implementacji systemów informatycznych. Technika ta stanowi innowacyjne oraz alternatywne podejście do klasycznych problemów zwiazanych z projektowaniem i implementacja aplikacji z wykorzystaniem paradygmatu programowania obiektowego. Podstawowym celem pracy jest analiza i ocena przydatności wzorca projektowego DCI. Badanie przydatności nowego narzędzia powinno być poprzedzone jego praktycznym wykorzystaniem, umożliwiajacym dokonanie obiektywnej oceny i skontrastowanie wyciagniętych wniosków z założeniami teoretycznymi. Dostępne w literaturze i publikacjach naukowych informacje odnośnie możliwości zastosowania DCI, oparte były dotychczas na rozważaniach teoretycznych oraz niewielkich przykładach, na postawie których autorzy starali się przedstawić ogólna ideę i zasadę działania wzorca. Ze względu na swoja prostotę, nie moga one służyć za podstawę oceny DCI. Z tego powodu jasno wynika konieczność stworzenia realnej aplikacji opartej o założenia paradygmatu DCI. W ramach pracy została zrealizowana, opisana w rozdziale trzecim, aplikacja DCI-Project wykorzystujaca mechanizmy i założenia wzorca projektowego DCI. Wprowadzenie do techniki programistycznej DCI opisano w rozdziale drugim. W tej części pracy przedstawiono również podstawowa terminologię oraz kluczowe cechy charakteryzujace wzorzec DCI. Zrealizowana w ramach pracy aplikacja DCI-Project została opisana w rozdziale trzecim. W pierwszej części rozdziału przedstawiono projekt i założenia dotyczace aplikacji. Następnie zaprezentowany został proces implementacji oraz testowania zrealizowanego systemu. W kolejnym rozdziale zawarto analizę przydatności oraz ocenę wzorca programistycznego DCI. Sformułowane wnioski oraz spostrzeżenia oparte zostały na doświadczeniach zdobytych podczas pracy nad aplikacja DCI-Project oraz studiami literaturowymi. Praca kończy się podsumowaniem.
2. Wzorzec projektowy DCI DCI (ang. Data, Context, Interaction) jest wzorcem programowania obiektowego opracowanym przez Trygve Reenskauga, emerytowanego profesora Uniwersytetu w Oslo [2]. DCI zakłada nowatorskie podejście do programowania obiektowego, umożliwiajace modelowanie systemów informatycznych w oparciu o role, jakie pełnia obiekty w czasie wykonania. Należy zaznaczyć, iż koncepcje stojace za wzorcem DCI sięgaja lat sześćdziesiatych, jednak nie zostały wcześniej wykorzystane praktycznie. DCI został sformułowany w 2008 roku jako efekt długoletnich prac autora nad projektem BabyUML [3]. Poczatkowym celem DCI było zapewnienie odpowiedniego poziomu szczegółowości w modelu MVC [4], który pozwoliłby na precyzyjne określenie zachowania systemu, nie w kontekście pojedynczych klas, lecz jako spójnej całości. DCI w dużej mierze korzysta z idei przedstawionych we wzorcu MVC i stanowi jego dopełnienie. Obecnie w pracach nad DCI, oprócz Trygve Reenskauga uczestniczy James O. Coplien [5] oraz szeroka rzesza osób skupiona na łamach grupy dyskusyjnej object-composition [6]. 2.1. Założenia Paradygmat zaproponowany przez Trygve Reenskauga zakłada całkowicie innowacyjne podejście do programowania obiektowego. Jednym z głównych celów stawianych przed DCI jest umożliwienie modelowania systemu w kontekście widzianym przez użytkownika tak, aby miał on wrażenie interakcji ze zbudowanym i widzianym przez siebie modelem systemu, nie zaś jego obiektowa implementacja. Wzorzec DCI dzieli system informatyczny na dwie główne części, oddzielajac statyczny kod modelu dziedziny ( to czym system jest ) od dynamicznego, opisujacego zachowania i funkcje ( to co system robi ). Jasne wydzielenie granic systemu ma na celu uniknięcie wysokiego stopnia rozproszenia kodu odpowiedzialnego za realizację algorytmów pomiędzy wieloma klasami modelu dziedziny, a także znaczaco wpłynać na zwiększenie czytelności kodu jako całości. DCI pozwala dokonać separacji zachowań systemu na poziomie przypadków użycia. Zachowania te reprezentowane sa przez role, które w kontekście przypadku użycia identyfikuja dany obiekt i jego funkcjonalność. Proponowane modelowanie w kontekście współpracujacych ze soba ról ma zapewnić użytkownikowi możliwość łatwego opisywania
2.2. Architektura 3 zachowań systemu poprzez wskazanie ról i zachodzacych pomiędzy nimi interakcji. W swojej publikacji [1] Reenskaug podkreśla, że DCI umożliwia bardziej intuicyjne modelowanie systemów oraz zatarcie granic pomiędzy użytkownikiem i komputerem, czyniac go niejako przedłużeniem ludzkiego umysłu. 2.2. Architektura W odróżnieniu od klasycznego podejścia do programowania obiektowego, w którym działanie programu polega na współpracy komunikujacych się ze soba obiektów, DCI sprowadza działanie systemu do sieci współpracujacych i powiazanych ze soba ról. W celu pełnego zrozumienia koncepcji paradygmatu DCI istotne jest wyróżnienie dwóch typów ról, które wykorzystywane sa w czasie modelowania systemu. Methodless Roles Bezstanowe identyfikatory, które umożliwiaja dostęp do obiektów systemu odgrywajacych dana rolę. Ponadto zapewniaja one dostęp do interfejsu obiektu identyfikowanego przez powiazan a z nim rolę i moga być uważane za pewna abstrakcję typu, która całkowicie pomija wewnętrzna strukturę obiektu. Methodful Roles Reprezentuja zachowanie systemu. Udostępniaja implementację metod powia- zanych z danym typem roli lub fragmentów algorytmów wykorzystywanych przez przypadek użycia. Methodful Roles składaja się na całość algorytmu i postrzegane sa jako struktura aplikacji. Z punktu widzenia programisty, wzorzec DCI dzieli system na trzy spójne części: dane, kontekst i interakcje, umożliwiajac w ten sposób przejrzysty podział odpowiedzialności pomiędzy poszczególne elementy systemu. Dane Część odpowiedzialna za dane składa się z podstawowych klas reprezentujacych statyczny model dziedziny. Klasy te pozbawione sa szczegółowej funkcjonalności powiazanej z konkretnym przypadkiem użycia, jednak moga zawierać implementacje metod zapewniajacych dostęp do podstawowych własności obiektu, czy też ogólnych zachowań, wspólnych dla całego systemu. Część danych często nazywana jest mikro baza danych, ponieważ zawiera jedynie klasy modelu dziedziny
2.2. Architektura 4 i powiazania pomiędzy nimi. Głównym celem takiej reprezentacji danych, dzięki odseparowaniu dynamicznych zachowań jest umożliwienie modelowania systemu w kontekście widzianym przez użytkownika, a nie na poziomie klas i interfejsów. Model danych prezentowany przez DCI w dużej mierze odpowiada modelowi znanemu ze wzorca MVC. Kontekst Kontekst odpowiedzialny jest za wykonanie przypadku użycia. W tym celu przechowuje on informacje o typach ról (Methodless Roles), które sa z nim zwiazane i dokonuje dynamicznego przypisania ich do obiektów w czasie wykonania. Przypisanie ról do odpowiednich obiektów powinno stanowić niepodzielna operację wykonywana w chwili zainicjowania kontekstu. Klasy kontekstu stosowane we wzorcu DCI stanowia przestrzeń nazw dla ról. W szczególności możliwe jest definiowanie wielu typów ról o jednakowych nazwach, pełniacych jednak odmienne funkcje na poziomie różnych przypadków użycia. Rozwiazanie to zapewnia wysoki stopień spójności kodu, umożliwiajac tworzenie przejrzystych interfejsów odpowiedzialnych jedynie za niezbędne działania. W obecnej formie paradygmat DCI zakłada istnienie dokładnie jednej instancji klasy kontekstu w czasie wykonania. Interakcje Interakcje wyrażaja zachowanie systemu i przedstawiaja to co system robi. Interakcja obrazuje pojedynczy przypadek użycia wraz ze wszystkimi jego ścieżkami wykonania. Na interakcje składaja się dynamicznie przypisywane role i zachodzace pomiędzy nimi zależności. W czasie wykonania przypadku użycia, z bioracymi w nim udział obiektami wiazana jest jedna lub wiele ról. Odegranie roli polega na połaczeniu statycznej części danych bieżacego stanu oraz wbudowanych funkcji obiektu ze specyficznymi zachowaniami, definiowanymi na poziomie roli. Obiekty, do których zostały przypisane role moga ze soba oddziaływać wywołujac odpowiednie metody identyfikatorów (Methodless Roles). Jednym z założeń paradygmatu DCI jest całkowita rezygnacja z klasycznego polimorfizmu na rzecz dynamicznego przypisywania zachowań określonych przez typ roli w czasie wykonania programu. Możliwość nadpisywania domyślnych implementacji zdefiniowanych w klasie bazowej została zastapiona poprzez wprowadzenie klas kontekstu.
2.3. Działanie systemu opartego na DCI 5 2.3. Działanie systemu opartego na DCI Działanie systemu wykorzystujacego wzorzec DCI w dużej mierze przypomina działanie systemu korzystajacego z architektury MVC. W obu przypadkach poczat- kowym czynnikiem inicjujacym odpowiedź systemu jest pewne zdarzenie zarejestrowane w środowisku wykonania. W wielu implementacjach jako naturalne środowisko wykonania wykorzystywany jest obiekt kontrolera znany ze wzorca MVC, który jest odpowiedzialny za rozpoznanie typu zdarzenia i podjęcie odpowiednich działań zwiazanych z jego obsługa. Zarejestrowanie zdarzenia powoduje powołanie lub odnalezienie instancji odpowiedniego kontekstu. Zdarzenia systemowe przechowuja wszystkie niezbędne informacje potrzebne do identyfikacji obiektu kontekstu oraz zwiazanego z nim przypadku użycia czy też algorytmu. 2.3.1. Przykład Poniższy przykład ilustruje implementację prostego przypadku użycia przedstawiajacego wykonanie przelewu z konta źródłowego na konto docelowe. Na wydruku 2.1 przedstawiona została jego implementacja w języku programowania Ruby. 1 module ContextAccessor 2 def context 3 Thread.current[:context] 4 end 5 6 def context=(ctx) 7 Thread.current[:context] = ctx 8 end 9 10 def execute_in_context 11 old_context = self.context 12 self.context = self 13 yield 14 self.context = old_context 15 end 16 end 17 18 class Account 19 attr_reader :balance 20 21 def initialize(balance) 22 @balance = balance 23 end 24 25 def increase_balance(amount) 26 @balance += amount 27 end 28 29 def decrease_balance(amount) 30 raise "Bad argument to withdraw" if amount < 0
2.3. Działanie systemu opartego na DCI 6 31 raise "Insufficient funds" if amount > balance 32 @balance = amount 33 end 34 end 35 36 37 module MethodlessMoneySource 38 def transfer_out(amount) 39 end 40 end 41 42 module MethodlessMoneySink 43 def transfer_in(amount) 44 end 45 end 46 47 module MoneySink 48 include MethodlessMoneySink, ContextAccessor 49 50 def transfer_in(amount) 51 self.increase_balance(amount) 52 puts "Transfer in", Time.now, amount, balance 53 end 54 end 55 56 module MoneySource 57 include MethodlessMoneySource, ContextAccessor 58 59 def transfer_out(amount) 60 raise "Insufficient funds" if balance < amount 61 self.decrease_balance(amount) 62 self.context.destination_account.transfer_in(amount) 63 puts "Transfer out", Time.now, amount, balance 64 end 65 end 66 67 class TransferMoneyContext 68 attr_reader :source_account, :destination_account, :amount 69 include ContextAccessor 70 71 def initialize(amount, source_account, destination_account) 72 @source_account = source_account 73 @destination_account = destination_account 74 @amount = amount 75 76 @source_account.extend(moneysource) 77 @destination_account.extend(moneysink) 78 end 79 80 def self.execute(amount, source_account, destination_account) 81 TransferMoneyContext.new(amount, 82 source_account,destination_account).execute 83 end 84 85
2.3. Działanie systemu opartego na DCI 7 86 def execute 87 execute_in_context do 88 source_account.transfer_out(amount) 89 end 90 end 91 end 92 93 account1 = Account.new(800) 94 account2 = Account.new(500) 95 96 TransferMoneyContext.execute(200, account1, account2) Wydruk 2.1. Na podstawie [7, strony 321-326] Dane Klasa Account (linie 18 34) stanowi model dziedziny wykorzystany w przykładzie. Zgodnie z ideologia DCI nie zawiera ona specyficznych funkcji dotyczacych wykonywania operacji bankowych, które zwiazane sa ze szczególnymi typami kont, a jedynie operacje wspólne dla wszystkich typów kont bankowych. Klasa Account stanowi więc abstrakcję konta bankowego, bez rozróżnienia, czy jest to konto źródłowe czy też docelowe. Methodless i Methodful Roles MethodlessMoneySource (linie 37 40) i MethodlessMoneySink linie (42 45) stanowia identyfikatory udostępniajace interfejs dla ról. Z punktu widzenia języka Ruby ich wyodrębnienie jako osobnych modułów nie jest konieczne, jednak ze względu na przejrzystość i spójność z ogólna definicja DCI zostały one uwzględnione w przykładzie. W odróżnieniu od Methodless Roles, MoneySink (linie 47 54) i MoneySource (linie 56 65) zawieraja implementacje metod specyficznych dla definiowanych przez nie ról. Moduły te posiadaja również dostęp do instancji bieżacej klasy kontekstu i powiazanych z nia identyfikatorów obiektów realizujacych przypadek użycia. Kontekst i interakcje Klasa TransferMoneyContext (linie 67 89) jest odpowiedzialna za dynamiczne zwiazanie ról z obiektami i zainicjowanie wykonania przypadku użycia. Działania wykonywane przez klasę kontekstu można przedstawić jako szereg czterech następujacych po sobie kroków: zidentyfikowanie obiektów, które będa brały udział w wykonaniu przypadku użycia, zwiazanie obiektów z rolami, które będa odgrywać podczas przypadku użycia,
2.4. DCI a inne techniki programistyczne 8 udostępnienie dostępu do identyfikatorów ról dla obiektów bioracych udział w przypadku użycia, zainicjowanie wykonania przypadku użycia. Na uwagę zasługuje również moduł ContextAccessor (linie 1 16), który odpowiedzialny jest za zarzadzanie aktywnym kontekstem i udostępnianie identyfikatorów obiektów bioracych udział w wykonywanym przypadku użycia. 2.3.2. Wymagania implementacyjne Wiele współczesnych języków programowania oferuje mechanizmy umożliwiajace implementację założeń paradygmatu DCI, w tym możliwość dynamicznego dodawania ról do obiektów w czasie wykonania. Implementacje DCI różnia się w zależności od wybranego języka i oferowanych przez niego mechanizmów. Dynamiczne języki programowania takie jak Ruby [8] i Python [9] umożliwiaja wstrzykiwanie kodu w czasie wykonania, podczas gdy implementacja DCI w języku C++ [10] wymaga wykorzystania mechanizmu szablonów i ma charakter bardziej statyczny. 2.3.3. Ograniczenia Jednym z ważniejszych ograniczeń narzucanych przez DCI jest liczba klas kontekstu, które moga być aktywne w tym samym czasie. Chociaż współczesne języki programowania zapewniaja mechanizmy pozwalajace na dynamiczne odwoływanie się do zdefiniowanych kontekstów, filozofia DCI nie dopuszcza możliwości istnienia więcej niż jednej aktywnej klasy kontekstu. Klasy kontekstu, których zadaniem jest dynamiczne wiazanie obiektów z ich rolami stanowia przestrzeń nazw dla typów ról. Oznacza to, że możliwe jest definiowanie ról o jednakowych nazwach lecz całkowicie odmiennym zachowaniu. Podobnie, role stanowia przestrzenie nazw dla definiowanych przez nie metod. Możliwość dynamicznego przypisywania metod specyfikowanych przez role do obiektów, a także przypisywania wielu ról do tego samego obiektu implikuje pewne ograniczenia na unikalność nazw metod. Nazwa metody zdefiniowanej na poziomie roli powinna być unikalna dla wszystkich ról o tej samej nazwie, a na poziomie obiektów, dla ról które sa do niego przypisane. Ograniczenie to pozwala uniknać sytuacji, w których do obiektu przypisywanych jest kilka metod o jednakowej nazwie, powodujac niejednoznaczne zachowanie. 2.4. DCI a inne techniki programistyczne Wiele wzorców i technik programistycznych od wielu lat wykorzystuje szereg koncepcji zbliżonych do tych, które sa prezentowane przez DCI.
2.4. DCI a inne techniki programistyczne 9 2.4.1. MVC Wzorzec MVC (Model Widok Kontroler) [4], został stworzony w latach siedemdziesiatych przez twórcę DCI - Trygve Reenskauga. Fundamentalna cecha tego wzorca jest model danych, niezależny od pozostałych komponentów aplikacji. Istota MVC jest oddzielenie części odpowiedzialnej za dostarczenie danych od warstwy prezentacji. W tym celu wzorzec ten wyróżnia trzy podstawowe elementy: model, widok, kontroler. Rozwiazania przedstawiane przez MVC skupiaja się przede wszystkim na jasnym oddzieleniu warstwy danych od pozostałych części systemu. W przeciwieństwie do DCI, wzorzec ten nie nakłada ograniczeń na sposób implementacji modelu danych klasy odpowiedzialne za dane zazwyczaj skupiaja większość funkcjonalności systemu. Porównujac wzorzec MVC z paradygmatem DCI, można zauważyć, iż cele stawiane przed obiema technikami sa odmienne. Model Widok Kontroler dokonuje oddzielenia warstwy odpowiedzialnej za prezentację od części systemu realizujacej operacje na danych, podczas gdy DCI dokonuje separacji danych od funkcji systemu. Jednakże cele zdefiniowane przez obie techniki nie wykluczaja się i moga zostać uznane za komplementarne. Twórca DCI, Trygve Reenskaug zaznacza, iż DCI stara się rozwiazać podstawowy problem zwiazany ze stosowaniem wzorca Model Widok Kontroler jakim jest brak wsparcia dla implementacji interakcji i zachowań systemu widzianych przez użytkownika. DCI nie należy traktować jako substytutu MVC, jednak można go wykorzystać jako dopełnienie i rozszerzenie MVC, wprowadzajace dodatkowy poziom abstrakcji pomiędzy kontroler a model danych. W swojej pracy opisujacej DCI [1], Reenskaug przedstawia środowisko wykonania aplikacji opartej na DCI, w której to kontroler znany z MVC stanowi centralny punkt, odpowiedzialny za dostarczenie obiektów do kontekstów. 2.4.2. Delegacja Delegacja [11] jest technika programistyczna wykorzystywana przez wiele wzorców projektowych. Wyróżnia ona dwa podstawowe obiekty odbiorcę oraz delegata, do którego kierowane sa wykonania operacji. Stosowane w DCI role sa koncepcyjnie zbliżone do delegatów to one definiuja dodatkowa funkcjonalność dla obiektu odbiorcy. Jednakże, pomiędzy obiektami delegatów a rolami przedstawionymi przez DCI istnieje zasadnicza różnica. Role sa bezstanowe i nie istnieja jako samodzielne
2.5. Założenia stawiane przed DCI 10 byty, podczas gdy delegaty stanowia pełnoprawne obiekty klas istniejacych w systemie. Ponadto, koncepcja ról przedstawiana przez technikę DCI rozwiazuje jeden z problemów wiaż acych się ze stosowaniem wzorca delegacji zjawisko tzw. self-schizofrenii, w której to tożsamość obiektu jest rozdzielona pomiędzy dwa obiekty odbiorcy i delegata. W przypadku DCI wszelka delegacja funkcjonalności odbywa się zawsze w kontekście obiektu powiazanego z przypisana do niego rola. Rozwiazanie to zapewnia, iż tożsamość obiektu jest zawsze jednoznaczna i nie prowadzi do sytuacji, w których identyfikacja obiektu jest niemożliwa. 2.5. Założenia stawiane przed DCI Poprzez podział systemu na niezależne jednostki kodu, DCI wymusza konsekwentne stosowanie dobrych praktyk programistycznych. Jak podkreślaja autorzy, DCI stanowi rozwiazanie problemów zwiazanych z niewłaściwym wykorzystaniem polimorfizmu. Jednym z wielu założeń stawianych przed paradygmatem DCI jest umożliwienie programistom komunikacji z użytkownikiem, poprzez wyrażenie funkcji systemu jako ról, które musi on spełniać. Kluczowym elementem jest wsparcie dla modelowania w kontekście sekwencji kroków prowadzacych do pewnego celu, które jest bardziej naturalne dla użytkownika końcowego i jego wizji systemu. Ponadto DCI ma na celu ułatwienie dokonania przejścia pomiędzy przypadkami użycia i wymaganiami użytkownika, a algorytmami i kodem aplikacji. Podział kodu na dane, kontekst i interakcje ma zapewnić wysoki stopień oddzielenia kodu podlegajacego intensywnym zmianom, od kodu statycznego, zachowujac jednocześnie możliwość łatwego testowania i dodawania nowych funkcji. Zdaniem twórców, DCI stanowi rozwiazanie dla wielu problemów zwiazanych z nadmiernym rozproszeniem kodu algorytmu i redundantnym kodem w klasach modelu dziedziny. Konsekwentne stosowanie DCI ma na celu zapewnienie poprawy czytelności kodu. W szczególności umieszczenie kodu algorytmów w jednym miejscu odpowiedniej klasie kontekstu ma ułatwić jego analizę i weryfikację.
3. Projekt i implementacja aplikacji DCI-Project Poniżej przedstawiono szczegółowy projekt oraz proces modelowania i implementacji systemu opartego o architekturę DCI. DCI-Project stanowi narzędzie umożliwiajace grupie osób wspólna pracę nad projektem. Osoby zaangażowane w pracę posiadaja możliwość tworzenia i przypisywania zadań do współpracowników, przegladu postępów prac oraz redagowania dokumentów i tekstów powstałych na różnych etapach realizacji projektu. Jednym z założeń aplikacji jest przypisanie projektu do konkretnej osoby właściciela projektu. Osoba ta jest odpowiedzialna za nadzorowanie przebiegu prac oraz posiada pełne spektrum uprawnień, na które składaja się: zarzadzanie utworzonymi projektami, dokumentami, współpracownikami oraz zadaniami. Oprócz wymienionych funkcji, użytkownik systemu ma możliwość komunikacji poprzez system komentarzy, a także obserwowania interesujacych go zadań czy też projektów. 3.1. Wymagania funkcjonalne Podstawowym zadaniem projektowanej aplikacji jest dostarczenie środowiska umożliwiajacego zbadanie i ocenę kluczowych cech paradygmatu DCI. Z tego względu lista wymagań funkcjonalnych została dobrana tak, aby możliwe było wykorzystanie kluczowych właściwości techniki DCI, jednocześnie pozostawiajac aplikację w pełni działajacym systemem, gotowym do wykorzystania przez użytkownika. Poniżej przedstawiono listę wymagań funkcjonalnych wraz z krótkim komentarzem: 1. Zarzadzanie projektami przegladanie użytkownik systemu posiada możliwość przegladania projektów znajdujacych się w systemie, tworzenie użytkownik systemu posiada możliwość utworzenia nowego projektu. W chwili utworzenia projektu użytkownik automatycznie staje się jego właścicielem. edycja właściciel projektu posiada możliwość jego edycji, w szczególności może on przekazać projekt innej osobie,
3.1. Wymagania funkcjonalne 12 przypisywanie użytkowników do projektu właściciel projektu może zarza- dzać osobami, które współuczestnicza w projekcie. 2. Zarzadzanie zadaniami przegladanie osoby zaangażowane w projekt posiadaja możliwość przegla- dania zadań, które zostały utworzone w ramach projektu, tworzenie osoby zaangażowane w projekt maja możliwość tworzenia i przypisywania do współpracowników zadań. Każde z tworzonych zadań jednoznacznie przynależy do projektu oraz do dwóch osób: zgłaszajacej i przydzielonej do wykonania zadania. edycja właściciel projektu oraz osoby, które powiazane sa z danym zadaniem posiadaja możliwość jego edycji - w szczególności zmianę osób powia- zanych z zadaniem oraz zmianę jego statusu. 3. Zarzadzanie dokumentami przegladanie użytkownik systemu posiada możliwość przegladania dokumentów, tworzenie osoby zaangażowane w projekt posiadaja możliwość tworzenia dokumentów tekstowych, które moga zostać powiazane z konkretnym projektem lub istnieć jako samodzielne elementy, edycja osoba, która utworzyła dokument posiada możliwość jego edycji. usunięcie - osoba, która utworzyła dokument posiada możliwość jego usunięcia. 4. Komentowanie komentowanie osoby zaangażowane w projekt posiadaja możliwość komentowania projektów, zadań oraz dokumentów, edycja komentarza autor komentarza posiada możliwość jego edycji. 5. Obserwowanie dodanie pozycji do obserwowanych osoby zaangażowane w projekt posiadaja możliwość dodawania projektów lub zadań do listy obiektów obserwowanych. Umieszczenie wyżej wymienionych obiektów na liście obserwowanych umożliwia użytkownikowi szybki dostęp do wybranych elementów. usunięcie pozycji z obserwowanych osoby zaangażowane w projekt posiadaja możliwość usunięcia elementów z listy obiektów obserwowanych, przegladanie pozycji obserwowanych.
3.2. Wymagania niefunkcjonalne 13 3.2. Wymagania niefunkcjonalne Oprócz przedstawionych powyżej wymagań, aplikacja DCI-Project ma na celu umożliwienie zweryfikowania przydatności i możliwości zastosowania DCI w realnych projektach informatycznych. W zwiazku z tym, aplikacja powinna dostarczyć środowisko stanowiace podstawę do oceny wpływu DCI na proces wytwarzania oprogramowania. 3.3. Modelowanie systemu opartego o DCI Na podstawie przedstawionych w rozdziale 3.2 wymagań funkcjonalnych opracowane zostały przypadki użycia opisane poniżej. 3.3.1. Diagramy przypadków użycia Użytkownik Współpracownik Właściciel projektu Rysunek 3.1. Aktorzy bioracy udział w przypadkach użycia
3.3. Modelowanie systemu opartego o DCI 14 Na rysunku 3.1 wyróżniono aktorów oraz zachodzace pomiędzy nimi relacje. Podstawowym aktorem jest użytkownik systemu. Zdefiniowane dla niego przypadki użycia sa dziedziczone odpowiednio przez współpracownika oraz właściciela projektu. Właściciel projektu, oprócz specyficznych dla siebie przypadków użycia, posiada możliwość realizacji przypadków użycia charakterystycznych zarówno dla użytkownika systemu jak i współpracownika. Przeglądanie projektów Tworzenie projektu Przeglądanie dokumentów Użytkownik Tworzenie dokumentów Rysunek 3.2. Diagram przypadków użycia użytkownik systemu Na rysunku 3.2 przedstawiono diagram przypadków użycia dla użytkownika systemu. Obejmuje on następujace przypadki użycia: projektów, a także przegladanie i tworzenie dokumentów. przegladanie i tworzenie Edytowanie projektu Zmiana właściciela projektu Dodanie elementu do obserwowanych Właściciel projektu Dodanie użytkowników do projektu Usunięcie użytkowników z projektu <<include>> <<include>> Usunięcie elementu z obserwowanych Rysunek 3.3. Diagram przypadków użycia właściciel projektu
3.3. Modelowanie systemu opartego o DCI 15 Na rysunku 3.3 przedstawiono diagram przypadków użycia dla właściciela projektu zawierajacy przypadki użycia zwiazane z zarzadzaniem projektem (edycja, zmiana właściciela, dodawanie i usuwanie użytkowników z projektu). Przedstawione na diagramie przypadki użycia dotyczace zarzadzania użytkownikami Dodanie użytkowników do projektu i Usunięcie użytkowników z projektu wykorzystuja zachowania zdefiniowane w istniejacych samodzielnie przypadkach użycia Dodanie elementu do obserwowanych oraz Usunięcie elementu z obserwowanych. Przeglądanie zadań Edycja zadania Zgłaszanie zadania Przeglądanie elementów obserwowanych Współpracownik Dodanie elementu do obserwowanych Usuwanie elementów z obserwowanych Edycja komentarza Dodanie komentarza Rysunek 3.4. Diagram przypadków użycia współpracownik
3.3. Modelowanie systemu opartego o DCI 16 Na rysunku 3.4 przedstawione zostały przypadki użycia dla osoby współuczestniczacej w pracy nad projektem. Sa to: przegladanie, tworzenie i edycja zadań, przegladanie i zarzadzanie elementami obserwowanymi (edycja, usuwanie) oraz dodawanie i edytowanie komentarzy. 3.3.2. Model dziedziny Na rysunku 3.5 przestawiony został model dziedziny aplikacji DCI-Project. Każdy z użytkowników systemu może być właścicielem jednego lub wielu projektów, a także jako współpracownik uczestniczyć w jego tworzeniu. Projekt posiada jednoznacznie określonego właściciela oraz może mieć zdefiniowane zadania, dokumenty czy też komentarze. Istotnym założeniem jest fakt, iż zadania w aplikacji DCI-Project tworzone sa zawsze w kontekście projektu i nie istnieja jako samodzielne obiekty. jest autorem posiada 1 1 Użytkownik 1 tworzy 0..* należy do Dokument 1..* 1 0..* 0..* jest przypisany jest właścicielem jest przypisany posiada jest przypisane 0..* posiada 0..* należy do 0..* Zadanie 0..* 1 należy do posiada Projekt 1 posiada 0..* 0..* posiada posiada 0..* jest przypisany jest przypisany 0..* Komentarz 0..* 0..* jest przypisany jest utworzony Rysunek 3.5. Model dziedziny Każde z utworzonych w systemie zadań może znajdować się w jednym ze stanów przedstawionych na rysunku 3.6. Wprowadzono dwa wyróżnione stany: poczat- kowy otwarte oraz końcowy zamknięte. zadanie znajduje się domyślnie w stanie otwarte. Każde nowo utworzone w systemie
3.4. Decyzje projektowe 17 Zamknięcie Zamknięcie Rozpoczęcie pracy Zakończenie pracy Zamknięcie Otwarte W toku Ukończone Zamknięte Zakończenie pracy Rysunek 3.6. Stany zadania 3.3.3. Klasy kontekstu i role Główna częścia systemu wykorzystujacego DCI sa klasy kontekstu, których zadaniem jest realizacja przypadków użycia i algorytmów. To na nich spoczywa odpowiedzialność za powiazanie obiektów z odpowiednimi rolami, a następnie zainicjowanie wykonania przypadku użycia, który reprezentuja. Zgodnie z założeniami DCI, każdy z wyspecyfikowanych przypadków użycia, wraz ze wszystkimi ścieżkami wykonania posiada swoje odzwierciedlenie w dokładnie jednej klasie kontekstu. Dla każdej z wyspecyfikowanych klas kontekstu należy wyznaczyć role, które zostana powiazane z obiektami bioracymi udział w wykonaniu klasy kontekstu. Naturalnymi kandydatami do ról sa aktorzy bioracy udział w przypadku użycia, jednakże DCI nie ogranicza programisty jedynie do ról aktorów. Obiekty biorace udział w wykonaniu klasy kontekstu powinny odwoływać i komunikować się ze soba wyłacznie za pomoca identyfikatorów ról. Założenie to wymusza istnienie co najmniej jednej roli dla każdego z obiektów, który zwiazany jest z dana klasa kontekstu. 3.4. Decyzje projektowe Istotnym aspektem dotyczacym każdego projektu informatycznego jest wybór technologii, z wykorzystaniem której zostanie on zrealizowany. Decyzja ta implikuje bowiem szereg możliwości oraz ograniczeń przed którymi staje programista. Na podstawie dokładnej analizy wymagań przestawionych w niniejszym rozdziale, a także własnych doświadczeń podjęto decyzję dotyczac a wyboru języka programowania i środowiska, w którym zostanie zrealizowana aplikacja DCI-Project. Zdecydowano, iż aplikacja zostanie wykonana z wykorzystaniem języka Ruby [8] oraz szkieletu aplikacyjnego Ruby on Rails [12]. Poniżej zaprezentowana została krótka charakterystyka obu technologii.
3.4. Decyzje projektowe 18 Język programowania Ruby Ruby jest dynamicznym, w pełni obiektowym, interpretowanym językiem programowania stworzonym przez Yuhikiro Matsumoto [13]. Dzięki szerokim możliwościom zastosowania oraz znakomitemu wsparciu społeczności, język ten cieszy się niegasnac a popularnościa. Wybór języka Ruby podyktowany był jego prosta i czytelna gramatyka, a także bogactwem narzędzi metaprogramowania. Ruby umożliwia zmianę i rozszerzanie wbudowanych w język klas, a także definiowanie własnych metod i modyfikację istniejacych obiektów w czasie działania aplikacji. Wymienione cechy języka ułatwiaja implementację mechanizmów zwiazanych z DCI. Szkielet aplikacyjny Ruby on Rails Ruby on Rails [12] jest szkieletem aplikacyjnym napisanym w języku Ruby. Umożliwia on tworzenie aplikacji internetowych z wykorzystaniem wzorca MVC. Ruby on Rails składa się z czterech głównych komponentów, które moga być również wykorzystywane indywidualnie: ActiveRecord Moduł zapewnia odwzorowanie relacyjno obiektowe tabel znajdujacych się w bazie danych w klasy modelu dziedziny. Moduł umożliwia wykonywanie operacji bazodanowych bez względu na wewnętrzna implementację systemu bazy danych, zapewniajac przez to wsparcie dla większości dostępnych systemów. ActiveRecord udostępnia szereg dodatkowych funkcji takich jak: zaawansowana kontrola poprawności danych czy też definiowanie relacji odzwierciedlajacych zwiazki pomiędzy tabelami w bazie danych. ActionPack Moduły odpowiedzialne za realizację kontrolera i widoku. Biblioteki wchodzace w skład ActionPack zajmuja się przekazaniem odpowiedzi przegladarki jako zapytania do odpowiednio kontrolera i warstwy prezentacji, które na podstawie przekazanych parametrów i konfiguracji aplikacji podejmuja decyzję o dalszym działaniu. ActiveSupport Zbiór bibliotek pomocniczych oraz rozszerzeń dla wbudowanych w język Ruby klas. ActionMailer Moduł zapewniajacy obsługę wiadomości e mail z poziomu aplikacji.
3.4. Decyzje projektowe 19 Realizacja wzorca MVC w Ruby on Rails Na rysunku 3.7 przedstawiono schemat architektury szkieletu aplikacyjnego Ruby on Rails. KONTROLER WIDOK MODEL Rysunek 3.7. Schemat architektury szkieletu aplikacyjnego Ruby on Rails Model Model danych w aplikacjach tworzonych z wykorzystaniem Ruby on Rails reprezentowany jest przez zbiór klas dziedziczacych po klasie bazowej ActiveRecord::Base. Klasy te stanowia odwzorowanie relacyjno obiektowe tabel zdefiniowanych w bazie danych. Wykorzystanie modułu ActiveRecord do implementacji modelu zapewnia szereg dodatkowych funkcji, które znaczaco usprawniaja proces tworzenia aplikacji. Sa to przede wszystkim: kontrola poprawności wprowadzanych danych oraz możliwość definiowania relacji pomiędzy poszczególnymi klasami modelu. Kontroler Kontrolery w aplikacjach wykorzystujacych Ruby on Rails zawieraja logikę aplikacji. Każdy kontroler skupia funkcje dotyczace pewnej konkretnej dziedziny (zazwyczaj każdy model posiada dedykowany kontroler) i definiuje metody akcje, które sa wyzwalane na podstawie żadania pochodzacego od użytkownika. Widok Widoki stanowia szablony znajdujace się w plikach zawierajacych połaczony kod HTML i języka Ruby. Przyjęta zasada jest, iż każdej akcji kontrolera odpowiada jeden plik szablonu. Widoki w Ruby on Rails moga być wyświetlane automatycznie na podstawie nazwy akcji kontrolera jak również możliwe jest sterowanie sposobem i formatem wyjściowej odpowiedzi.
3.5. Implementacja 20 3.5. Implementacja Implementacja aplikacji DCI-Project została podzielona na dwie fazy. W pierwszej skupiono się na opracowaniu architektury aplikacji oraz implementacji pomocniczego modułu DCI, w drugiej zaś na realizacji właściwego systemu i jego testowaniu. 3.5.1. Architektura aplikacji Pierwszym krokiem podczas implementacji aplikacji DCI-Project było opracowanie architektury, na której została ona oparta. Ze względu na wybór narzędzia Ruby on Rails, architektura aplikacji została oparta i dostosowana do schematu Model Widok Kontroler. KONTROLER ROLE KONTEKST WIDOK MODEL/DANE Rysunek 3.8. Schemat architektury aplikacji DCI-Project Rysunek 3.8 przedstawia architekturę i powiazania pomiędzy poszczególnymi komponentami aplikacji DCI-Project. Przedstawiona architektura stanowi rozszerzenie architektury szkieletu Ruby on Rails zaprezentowanej na rysunku 3.7. Zasadnicza różnicę stanowi zmiana sposobu komunikacji pomiędzy poszczególnymi komponentami aplikacji. Wykorzystanie architektury Model Widok Kontroler jako podstawy do realizacji aplikacji wykorzystujacej DCI wprowadza dodatkowa warstwę abstrakcji i modyfikuje sposób przepływu informacji pomiędzy poszczególnymi elementami systemu.
3.5. Implementacja 21 Tradycyjny obiekt kontrolera przestaje być spoiwem łacz acym interfejs użytkownika z modelem danych. Rolę tę przejmuje kontekst, który to na podstawie parametrów uzyskanych od kontrolera inicjuje proces interakcji dynamicznie łacz ac role z obiektami modelu dziedziny. Jednym z założeń dotyczacych architektury aplikacji DCI-Project jest brak bezpośrednich odwołań do obiektu kontrolera i widoku przez klasy kontekstu. Decyzja ta podyktowana jest chęcia uczynienia klas kontekstu całkowicie niezależnymi modułami, które sa przenośne i łatwiejsze w późniejszym testowaniu. Szczegóły implementacyjne dotyczace poszczególnych komponentów aplikacji DCI-Project zostały przedstawione w dalszej części rozdziału. 3.5.2. Moduł DCI W ramach aplikacji DCI-Project zrealizowany został pomocniczny moduł, którego celem było udostępnienie wspólnego interfejsu dla klas kontekstu i ról. Moduł ten stanowi zbiór modułów języka Ruby, które moga zostać wykorzystane w dowolnej aplikacji opartej na technice DCI. Możliwe rozwiazania Wykorzystanie mechanizmu extend języka Ruby Ruby oferuje możliwość rozszerzania obiektów o funkcjonalność zdefiniowana na poziomie modułu za pomoca wbudowanej metody extend. Metoda ta przedstawiana jest w większości przykładów ilustrujacych działanie mechanizmu DCI głównie z powodu oczywistego znaczenia słowa extend rozszerzenia obiektu o dodatkowe funkcje, co jest podstawowym celem DCI. Pomimo prostej implementacji, rozwiazanie to niesie ze soba zasadnicza wadę, zwiazan a z brakiem analogicznego do extend mechanizmu umożliwiajacego usunięcie metod i atrybutów dodanych do instancji. Problem ten uwidacznia się szczególnie w przypadku korzystania z zagnieżdżonych klas kontekstu. Obiekt aktor odgrywajacy rolę podczas realizacji przypadku użycia powinien być z nia zwiazany tylko na poziomie bieżacego kontekstu. W przypadku zagnieżdżonych klas kontekstu i stosowania metody extend obiekt posiada metody zdefiniowane na poziomie nadrzędnej klasy kontekstu, co narusza jedna z podstawowych zasad wzorca DCI. Injectionless DCI Alternatywna metoda umożliwiajac a implementację DCI, pozbawiona wyżej wymienionych wad, jest zastosowanie techniki opisanej przez Trygve Reenksauga
3.5. Implementacja 22 na łamach grupy dyskusyjnej dci-evolution [14]. Głównymi założeniami wspomnianej implementacji sa: brak dynamicznego dodawania metod do obiektu w czasie wykonania, metody zdefiniowanie na poziomie roli (Methodful Role) zwiazane sa z rola, a nie z obiektem, metody roli maja pierwszeństwo nad metodami instancji obiektu, odwołania do metod instancji jak i metod roli odbywaja się z wykorzystaniem identyfikatora roli, nazwa roli jest synonimem dla obiektu, który jest zwiazany z ta rola. Ze względu na wady rozwiazania wykorzystujacego metodę extend języka Ruby, w aplikacji DCI-Project zdecydowano się na implementację mechanizmów DCI z wykorzystaniem drugiej z przedstawionych technik Injectionless DCI. Bazowy moduł kontekstu 1 module ContextAccessor 2 def context 3 Thread.current[:context] 4 end 5 end 6 7 module Context 8 include ContextAccessor 9 10 attr_accessor :roles 11 12 def role(role, object) 13 if @roles.nil? 14 @roles = Hash.new 15 end 16 @roles[role] = object 17 end 18 19 def context=(context) 20 Thread.current[:context] = context 21 end 22 23 def interaction 24 old_context = self.context 25 self.context = self 26 response = yield 27 self.context = old_context 28 response 29 end 30 end Wydruk 3.1. Bazowy moduł kontekstu
3.5. Implementacja 23 Przedstawiony na wydruku 3.1 moduł Context dostarcza bazowa funkcjonalność dla tworzonych w systemie klas kontekstu. Interfejs udostępnia trzy metody: role wiaż ac a obiekt wraz z jego rola, interaction główna metodę, w której następuje ustawienie bieżacego kontekstu oraz jego definicja oraz pomocnicza context. Ponadto moduł Context udostępnia strukturę przechowujac a powiazania ról i obiektów. Bazowy moduł roli 1 class String 2 def constantize 3 self.split("::").inject(module) { acc, val acc.const_get(val)} 4 end 5 end 6 7 module Role 8 include ContextAccessor 9 10 def method_missing(method, args, &block) 11 raise "Context not specified" unless context && context.is_a?(self_context_class) 12 if context.roles[self].respond_to?(method) 13 context.roles[self].send(method, args, &block) 14 else 15 super 16 end 17 end 18 19 def self_context_class 20 self.to_s.chomp(self.to_s.gsub(/^. ::/, )).constantize 21 end 22 end Wydruk 3.2. Bazowy moduł roli Bazowy moduł Role, przedstawiony na wydruku 3.2, dostarcza niezbędne funkcje umożliwiajace implementację ról na poziomie klas kontekstu. Głównym założeniem poczynionym przy realizacji powyższego modułu jest powiazanie go z mechanizmem odpowiedzialnym za obsługę kontekstu. Umożliwia to realizację jednego z założeń dotyczacych DCI, według którego role nie powinny istnieć poza zasięgiem klas kontekstu. Realizacja techniki Injectionless DCI w języku Ruby wymagała użycia mechanizmów metaprogramowania, dzięki którym uzyskano odpowiednie zachowanie ról i powiazanych z nimi obiektów. Kluczowym aspektem przedstawionej implementacji jest wykorzystanie metody method_missing języka Ruby, która umożliwiła sterowanie przepływem wywołania metod roli i metod instancji obiektu aktora.
3.5. Implementacja 24 Schemat działania metody method_missing (wydruk 3.2, linie 10 17) jest następujacy: w przypadku wywołania niezdefiniowanej metody na rzecz roli następuje sprawdzenie czy metoda ta została zdefiniowana na poziomie powiazanego obiektu, i w razie pozytywnej odpowiedzi następuje jej wywołanie. Rozwiazanie, choć koncepcyjnie zbliżone do delegacji, pozbawione jest jej wad i zapewnia efektywna realizację koncepcji Injectionless DCI. 3.5.3. Dane W aplikacji DCI-Project nie zdecydowano się na wprowadzenie osobnego, niezwiazanego ze szkieletem Ruby on Rails, modelu danych. Decyzja ta podyktowana była chęcia implementacji modelu zgodnie z konwencja tworzenia aplikacji z wykorzystaniem Ruby on Rails oraz uniknięciem duplikacji i tworzeniem nadmiarowej warstwy abstrakcji. Podczas implementacji części systemu odpowiedzialnego za dane wykorzystany został przedstawiony wcześniej moduł ActiveRecord szkieletu Ruby on Rails. Modele tworzone z wykorzystaniem modułu ActiveRecord pozwalaja na definiowanie powiazań pomiędzy nimi. Obiekty klas, w których zostały zdefiniowane relacje automatycznie uzyskuja dodatkowe metody umożliwiajace bezpośredni dostęp do powiazanych obiektów. 3.5.4. Kontekst, role i interakcje Inicjalizacja i wykonanie klas kontekstu zostały osadzone we wnętrzu metod kontrolera akcjach. Kontroler, na podstawie informacji o żadaniu użytkownika, wywołuje jedna ze swoich akcji jednocześnie inicjalizujac klasę kontekstu i rozpoczyna wykonanie przypadku użycia. Zadaniem kontrolera jest również dostarczenie obiektów oraz dodatkowych parametrów, które sa wymagane przez klasę kontekstu. Inicjalizacja kontekstu Każda z zaimplementowanych klas kontekstu posiada konstruktor, którego argumentami sa obiekty biorace udział w przypadku użycia oraz dodatkowe, niezbędne parametry. Konstruktor kontekstu jest także miejscem, w którym następuje powiazanie obiektów z ich rolami.
3.5. Implementacja 25 Role Role obiektów bioracych udział w wykonaniu przypadku użycia definiowane sa na poziomie klasy kontekstu i zawieraja tylko metody, które sa niezbędne do wykonania danego przypadku użycia. Oznacza to, iż rola o tej samej nazwie może występować w wielu klasach kontekstu, lecz stanowi ona całkowicie odrębny moduł. Zgodnie z zasadami przedstawianymi przez DCI, rola wiazana jest z obiektem za pomoca atomowej operacji. Jednakże, w czasie implementacji aplikacji DCI-Project napotkano problem zwiazany z przypisaniem roli w ramach tej samej klasy kontekstu do więcej niż jednego obiektu. Ze względu na specyfikę DCI, niemożliwe jest użycie tej samej nazwy roli do wykonania więcej niż jednego atomowego zwiazania jej z obiektem. Na wydruku 3.3 (linia 7) przedstawiono sposób, w który rozwia- zano to ograniczenie w aplikacji DCI-Project rola przypisywania jest do kolekcji obiektów jako całości, a nie do jej poszczególnych elementów. 1 class CreateIssueContext 2 include Context 3 4 def initialize(reporter, assignee, params) 5 role Reporter, reporter 6 role Assignee, assignee 7 role IssueCollabolators, [reporter, assignee] 8 9 @project_id = params[:project_id] 10 @params = params[:issue] 11 end 12 13 def execute 14 interaction do 15 issue = Reporter.create_issue(@project_id, @params) 16 IssueCollabolators.watch_issue(issue.id) 17 Response.new(issue) 18 end 19 end 20 21 module Reporter 22 extend Role 23 class << self 24 def create_issue(project_id, params) 25 project = Project.find(project_id) 26 issue = project.issues.create(params.merge(:reporter_id => id)) 27 issue 28 end 29 end 30 end 31 32 module Assignee 33 extend Role 34 end 35
3.5. Implementacja 26 36 module IssueCollabolators 37 extend Role 38 class << self 39 def watch_issue(issue_id) 40 context.roles[self].uniq.each do collabolator 41 WatchIssueContext.new(collabolator, issue_id).execute 42 end 43 end 44 end 45 end 46 end Wydruk 3.3. Kontekst tworzenia zadania Komunikacja kontekstu z innymi komponentami Klasy kontekstu w aplikacji DCI-Project nie posiadaja referencji do obiektów kontrolera, z poziomu którego zostały zainicjowane i wykonane. Jednakże, ze względu na specyfikę zastosowanej architektury aplikacji, kontroler musi znać wynik działania kontekstu, aby na jego podstawie wyświetlić odpowiedni widok. Informacja o rezultacie wykonania kontekstu jest istotna szczególnie w przypadku kontekstów, które zawieraja kilka możliwych ścieżek wykonania. W zrealizowanej aplikacji zagadnienie komunikacji kontroler kontekst zostało rozwiazane poprzez wprowadzenie dodatkowego obiektu odpowiedzi, który jest zwracany jako wynik działania klasy kontekstu. Odpowiedź zawiera obiekt lub obiekty, które brały udział w wykonaniu kontekstu oraz dodatkowe parametry, które moga być następnie zinterpretowane przez kontroler do podjęcia decyzji o wykonaniu dodatkowej akcji czy też wyświetlenia widoku. Implementacja relacji pomiędzy przypadkami użycia Klasy kontekstu, podobnie jak przypadki użycia, moga pozostawać ze soba w relacjach. Umożliwia to ich ponowne wykorzystanie i znaczaco zmniejsza ilość redundantnego kodu. Przykładem klasy kontekstu wykorzystujacej relację zawierania, jest klasa kontekstu odpowiadajaca przypadkowi użycia zwiazanego z utworzeniem i przypisaniem zadania. Wykorzystuje ona, istniejac a jako samodzielny przypadek użycia, klasę kontekstu reprezentujac a obserwowanie zadania. Klasy kontekstu, które wykorzystuja relację zawierania moga być inicjalizowane na poziomie metod ról, jak również same moga odgrywać rolę. W aplikacji DCI-Project wykorzystano pierwsza z wymienionych możliwości. Implementacja zagnieżdżonych klas kontekstu pozostajacych ze soba w relacji zawierania została przedstawiona na wydruku 3.3 (linie 36 45).
3.6. Testy aplikacji 27 3.6. Testy aplikacji Wraz z tworzeniem kodu aplikacji weryfikowana była poprawność implementacji. W ramach procesu testowania przeprowadzone zostały trzy rodzaje testów: testy modułu DCI, testy klas kontekstu oraz testy integracyjne weryfikujace poprawność zachowania aplikacji. W procesie testowania wykorzystane zostały biblioteki Rspec [15] oraz Capybara [16], które dostarczaja programiście specjalizowany język dziedzinowy, umożliwiajacy przejrzysta implementację przypadków testowych. Testy modułu DCI Przed przystapieniem do testowania aplikacji DCI-Project wykonane zostały testy zaimplementowanego modułu DCI. Ich celem była weryfikacja poprawnej implementacji przedstawionej w rozdziale 3.5.2 techniki Injectionless-DCI. Podczas testów modułu sprawdzone zostały następujace elementy: poprawność wiazania ról do pojedynczych obiektów oraz kolekcji obiektów, poprawność wykonania zagnieżdżonych kontekstów, poprawność zwracanych przez kontekst wartości. Testy kontekstu Celem testów kontekstu, była weryfikacja poprawności implementacji funkcjonalności wyspecyfikowanych w procesie projektowania aplikacji DCI-Project. Dla każdej z zaimplementowanych klas kontekstu opracowany został przypadek testowy sprawdzajacy poprawność wykonania przypadku użycia dla wszystkich jego możliwych ścieżek wykonania. Fakt, iż role obiektów nie sa samodzielnymi bytami, a istnieja jedynie na poziomie kontekstu, implikuje konieczność testowania kontekstu jako całości. W szczególności nie jest możliwa weryfikacja poprawności implementacji poszczególnych metod ról z wykorzystaniem testów jednostkowych. W zwiazku z przedstawionym ograniczeniem opracowany został następujacy scenariusz testowania klas kontekstu: przygotowanie obiektów bioracych udział w wykonaniu kontekstu, incjalizacja kontekstu, rozpoczęcie interakcji, sprawdzenie poprawności zwróconego przez kontekst wyniku. Przedstawione rozwiazanie weryfikuje poprawność wykonania interakcji oraz uzyskanej odpowiedzi. Testy dotyczace klas kontekstu zostały przeprowadzone poza ich docelowym środowiskiem wykonania kontrolerem.