• Nie Znaleziono Wyników

Index of /rozprawy2/10651

N/A
N/A
Protected

Academic year: 2021

Share "Index of /rozprawy2/10651"

Copied!
98
0
0

Pełen tekst

(1)Akademia Górniczo-Hutnicza Wydział Elektrotechniki, Automatyki, Informatyki i Inżynierii Biomedycznej. Rozprawa doktorska. Wykorzystanie transformacji grafowych do wykluczania konfliktów w czasie refaktoryzacji prowadzonej w środowisku rozproszonym mgr Adrian Nowak. Promotor: dr hab. Leszek Kotulski, prof. AGH. Kraków 2013.

(2) Składam gorące podziękowania Panu Profesorowi Leszkowi Kotulskiemu, za opiekę naukową, inspirujące dyskusje i cenne uwagi.. Adrian Nowak.

(3) Spis treści 1. Wstęp. 6. 1.1 Rola refaktoryzacji. 7. 1.2 Problem. przeprowadzania. refaktoryzacji. podczas. pracy. 9. zespołowej. 2. 3. 1.3 Cel badań. 11. 1.4 Organizacja rozprawy. 12. Refaktoryzacja w środowisku rozproszonym. 14. 2.1 Pojęcie refaktoryzacji. 14. 2.2 Klasyfikacja refaktoryzacji. 17. 2.3 Proces wprowadzania refaktoryzacji. 18. 2.4 Środowisko pracy zespołowej. 19. 2.4.1 Łączenie zmian po refaktoryzacji. 20. 2.4.2 Kompozycje oraz łańcuchy przekształceń. 23. 2.4.3 Związki między refaktoryzacjami. 24. 2.5 Stan dotychczasowych badań. 26. Koncepcja systemu kontroli refaktoryzacji. 29. 3.1 Własności systemu. 29. 3.2 Grafowa reprezentacja oprogramowania. 31. 3.3 Repozytorium grafowe. 32. 3.3.1 Formalizm transformacji grafowych. 33. 3.3.2 Kontrola wykonania szeregu transformacji. 35. 3.

(4) 3.4 Architektura systemu z rozproszonym repozytorium grafowym. 4. 5. 6. 37. 3.4.1 Przebieg wykonania przekształceń. 39. 3.4.2 Tryb synchronizacji zmian z repozytorium. 39. 3.5 Podsumowanie. 41. Implementacja repozytorium grafowego. 42. 4.1 Struktura grafu oprogramowania. 42. 4.2 Konstrukcja grafu. 47. 4.2.1 Przekształcenia generujące graf oprogramowania. 49. 4.2.2 Refaktoryzacje. 53. 4.2.3 Przekształcenia usuwające komponenty i relacje. 57. 4.2.4 Język grafów oprogramowania. 58. 4.3 Efektywna implementacja za pomocą gramatyki ETPL(k). 60. 4.4 Podsumowanie. 62. Eliminacja konfliktów. 64. 5.1 Występowanie konfliktów po refktoryzacji. 64. 5.2 Przyczyny występowania konfliktów. 67. 5.3 Schemat wykluczania konfliktów. 69. 5.4 Mechanizmy systemu do pokonywania pozostałych problemów. 72. 5.5 Podsumowanie. 73. Zarządzanie łańcuchami refaktoryzacji. 74. 6.1 Warunek zgodności łańcuchów refaktoryzacji. 74. 6.2 Sterowanie wykonaniem łańcucha. 75. 6.3 Zarządzanie przekształceniami wprowadzanymi w środowisku. 77. rozproszonym. 7. 6.4 Podsumowanie. 81. Podsumowanie. 83 4.

(5) 7.1 Otwarte problemy badawcze. 85. A. Podstawowe refaktoryzacje. 87. B. Meta-model oprogramowania. 90. Bibliografia. 92. 5.

(6) Rozdział 1 Wstęp Konieczność ewolucji oprogramowania jest oczywistym następstwem procesu rozwoju firmy, rozszerzenia działalności czy pojawienia się nowych przepisów prawnych. Względy ekonomiczne i potrzeba szybkiego dostosowania się do nowych wymagań nie pozwalają zaprzepaścić już wykonanej pracy. Bardzo ważnym zadaniem zespołów projektantów i programistów staje się zatem pielęgnacja i rozwój już istniejących systemów informatycznych [5, 34, 51, 81]. Szacuje się, iż działy IT przeznaczają wręcz blisko 80 procent swojego budżetu na utrzymanie i ewolucję systemów informatycznych [67, 84]. Nieuchronnie po serii modyfikacji system przestaje odpowiadać pierwotnym zamierzeniom projektantów, a jego kod staje się coraz bardziej rozbudowany i skomplikowany [5, 6, 14, 27]. Złożoność implementowanych systemów, kwestie zapewnienia jakości, a przy tym odpowiedniej wydajności pracy zespołu, powodują iż zapanowanie nad projektem i prowadzenie prac rozwojowych jest zadaniem bardzo trudnym. Taki stan rzeczy skłania nie tylko do (1) poszukiwania odpowiednich metodyk tworzenia i pielęgnacji oprogramowania, ale także do (2) opracowywania coraz to efektywniejszych sposobów pracy i współpracy członków zespołów, a co za tym idzie poszukiwania nowych możliwości rozwoju środowisk pracy dla projektantów i programistów. Refaktoryzacja jest techniką wspomagająca zadanie pierwsze, natomiast w pracy zdefiniujemy środowisko wspomagające zadanie drugie.. 6.

(7) 1.1 Rola refaktoryzacji Jak zauważa Sunye [78], w ostatnich latach, za dobrą praktykę uznaje się podział rozwoju oprogramowania na co najmniej dwa etapy: . przygotowanie i restrukturyzację projektu bez wprowadzania jakiejkolwiek nowej funkcjonalności;. . zmianę działania oprogramowania poprzez rozbudowę funkcjonalności.. Pierwszy etap, w przypadku oprogramowania zorientowanego obiektowo, to proces refaktoryzacji (ang. refactoring) [14, 27, 42, 56, 78]. Proces ten wskazywany jest jako jeden z głównych elementów takich nowoczesnych metodyk wytwarzania oprogramowania jak programowanie ekstremalne (ang. Extreme Programming, XP) [2, 51] czy Scrum [7] opartych o koncepcje zwinnych procesów tworzenia (ang. Agile) [79, 9] – gdzie z założenia budowa systemu odbywa się na zasadzie iteracji przy ciągłej inspekcji wymagań. Wszystko zgodnie z zasadą, iż dobry projekt nie powstaje od razu, a rozwija się wraz z usystematyzowaniem wiedzy na temat modelowanych procesów i doświadczeniem zdobywanym przez zespół (dotyczy to również wiedzy samego zleceniodawcy, który ma możliwość doprecyzowania wymagań w trakcie tworzenia systemu). Okazuje się bowiem, że tradycyjne podejście do projektowania systemów informatycznych (model kaskadowy) [5, 6, 25, 75] rzadko prowadzi do sukcesu [5, 79, 84] – prawie nigdy nie ma tak komfortowej sytuacji by system dało się zaprojektować zakładając, iż wymagania pozostaną niezmienne. Jeżeli zatem nie korzysta się z refaktoryzacji to pojawia się presja na stworzenie projektu bez zarzutów, gdyż wszystkie późniejsze zmiany byłyby kosztowne. Prowadzi to z kolei do tzw. projektowania na wyrost [9], kiedy poszukuje się zbyt elastycznych i bardziej skomplikowanych rozwiązań tracąc jednak tylko czas, gdyż okazują się one w praktyce nigdy niepotrzebne. Z drugiej strony nawet najbardziej zagorzali zwolennicy metodyk lekkich nie zrezygnują całkowicie z projektowania [27, 59]. Brak dokumentacji jest przyczyną problemów komunikacyjnych w zespole (często także zmieniającym swój skład), z kolei zbyt duże zdyscyplinowanie hamuje rozwój i elastyczność systemu. Właściwym podejściem w warunkach zmieniających się wymagań, wydaje się zatem odpowiednie łączenie metodyk lekkich z tradycyjnymi jak na przykład zaleca metodyka XPrince [59]. Warto projektować lecz nie szukać bezwzględnie jedynego właściwego 7.

(8) rozwiązania. W miarę tworzenia system staje się bowiem coraz bardziej zrozumiały, a wprowadzenie zmian, dzięki technice refaktoryzacji, jest znacznie mniej kosztowne. Oczywiście wciąż warto dążyć do najlepszego projektu, jednak nie trzeba zakładać i nie implementować go od razu na początku. Jak się okazuje na przyrostowym tworzeniu oprogramowania w praktyce opierają się nawet metodyki takie jak Rational Unified Process - RUP (model spiralny) [39, 51, 52] czy PSP/TSP [35, 68]. Martin Fowler [27] określa refaktoryzację wprost jako technikę umożliwiającą ograniczenie nakładów pracy podczas przeprojektowywania oprogramowania. Mimo, iż same refaktoryzacje nie zmieniają funkcjonalności systemu, etap ich wprowadzania okazuje się mieć ogromy wpływ na utrzymywanie wysokiej jakości oprogramowania – poprzez poprawę wewnętrznej struktury i zmniejszanie złożoności [27, 54]. Lepsza czytelność kodu (bardzo istotna w kontekście w.w. metodyk zwinnych, promujących czytelność kodu ponad sporządzanie dokumentacji), pomaga programistom w odkrywaniu błędów, sprawniejszym programowaniu i łatwiejszym zrozumieniu przebiegu sterowania w kodzie [27]. Refaktoryzacja usprawnia nie tylko proces budowy systemu lecz przede wszystkim przygotowuje go do późniejszej pielęgnacji i dalszego rozwoju, przyczyniając się tym samym do znacznego obniżenia również kosztów tych procesów. Niezbędnym uzupełnieniem refaktoryzacji są testy weryfikujące skutki wprowadzania przekształceń refaktoryzujących. Refaktoryzacja wraz z testami jednostkowymi [2, 3] (zaprojektowanymi dla pojedynczych elementów kodu) składają się na technikę programowania ekstremalnego zwaną programowaniem sterowanym testami (ang. Test Driven Development – TDD) [3]. Proces ewolucji systemu staje się dzięki niej bardziej kontrolowany, a odpowiednie pokrycie kodu testami przyczynia się do tworzenia wysokiej jakości, niezawodnego oprogramowania. Podejście TDD pozwala również na wczesne uzyskanie pierwszej wersji systemu, skutkuje zdyscyplinowaniem stylu pracy oraz eliminuje projektowanie na wyrost. Testy sprawdzają się doskonale ze względu na brak innej formalnej możliwości definiowania lub badania zachowania działania programu, są również często jedynym wyznacznikiem wymaganej niezmienności działania po refaktoryzacji. Ich stosowanie wspiera także swobodne przeprowadzanie „eksperymentów” w projekcie. Niestety sam proces tworzenia takich testów pozostaje manualny (programowanie), choć już wykonywane są one najczęściej w sposób zautomatyzowany. 8.

(9) Z uwagi na możliwość rozszerzenia funkcjonalności budowanego systemu warto nadmienić, iż z refaktoryzacją wiąże się ogólniejsze pojęcie, od którego się wywodzi, a mianowicie – restrukturyzacja [34]. Restrukturyzacja nie ogranicza się do transformacji programów obiektowych, nie musi również wprowadzać restrykcyjnych ograniczeń odnośnie niezmienność działania programu. – są to wszelkie. przekształcenia zachowujące funkcjonalność operujące na tym samym poziomie abstrakcji. Dzięki przedstawionym własnościom oraz TDD refaktoryzacja została uznana za jedną z kluczowych technik ewolucji systemów obiektowych. Pozwala przygotować oprogramowanie na przyszłe rozszerzenia, podnosi wartość projektu programu, czyni go łatwiejszym do analizy, pozwala wyszukać błędy czy wreszcie znacznie sprawniej programować [1, 16, 27, 42]. Zyskała przez to szeroką aprobatę w przemyśle, a także wzbudziła zainteresowanie w środowisku akademickim. Wyniki przeprowadzonych badań pokazały, że koszty wprowadzania refaktoryzacji są równoważone przez oszczędności wynikające z jej stosowania [83]. Badania systemów dużej skali [13] potwierdzają z kolei, iż większość całkowitych kosztów tworzenia oprogramowania zajmuje pielęgnacja [5, 34, 75, 84], co bardziej zdumiewające udział ten zwiększa się wraz z coraz nowocześniejszymi metodami jakimi system jest tworzony [33] (wyjaśnieniem tej obserwacji jest fakt, iż nowoczesne systemy muszą coraz powszechniej zmagać się ze zmianą wymagań i odpowiednio do nich ewoluować).. 1.2 Problem przeprowadzania refaktoryzacji podczas pracy zespołowej Każda z refaktoryzacji może, a często wręcz musi, operować na całym projekcie (kodzie oraz modelu programu). Przykładowo zmieniając nazwę metody efekty powinny pojawić się we wszystkich miejscach w powiązanym kodzie, gdzie metoda ta jest wywoływana (nie zawsze będzie to wprost widoczne z poziomu modelu oprogramowania). Bardzo istotnym przy przeprowadzaniu refaktoryzacji jest zatem odpowiednie wsparcie ze strony narzędzi. Okazują się one użyteczne poczynając już od etapu identyfikacji przekształcanych fragmentów projektu, a kończąc na weryfikacji poprawności po wprowadzeniu transformacji. Obecnie jednak funkcjonalność dostępnych narzędzi ogranicza się niestety tylko do automatyzacji 9.

(10) wykonania przekształcenia na wskazanych przez użytkownika komponentach. Wśród cieszących się popularnością narzędzi automatyzujących refaktoryzację wymienić należy: Refactoring Browser [70] dla języka Smalltalk (było to pierwsze narzędzie tego typu), środowisko Eclipse [18] oraz IntellijIDEA [37] dla języka Java. Wszystkie te narzędzia wspomagają pojedyncze przekształcenia refaktoryzujące, a do tego wyłącznie z perspektywy odosobnionego stanowiska pracy, choć bez wykorzystania tych podstawowych własności refaktoryzacja okazuje się być znacznie bardziej kosztowna i wręcz dość „ryzykowna” [27]. Tymczasem w stosunku do środowisk programistycznych (szczególnie aspirujących do miana wydajnych i nowoczesnych) niewątpliwie należałoby oczekiwać znacznie większego wsparcia, a dzięki temu jeszcze lepszej redukcji kosztów wprowadzania refaktoryzacji. Tworząc bowiem oprogramowanie na większą skalę, w dużych projektach, niemal zawsze mamy do czynienia z równoległą pracą zespołów ludzi, przy tym często rozproszonych geograficznie [4, 75]. Niezwykle ważna jest możliwość sprawnej koordynacji prac członków zespołów. W modelu pracy z centralnym serwerem plików potrzeba ta jawi się szczególnie na etapie łączenia zmian (ang. software merging) [53] wprowadzanych w środowiskach lokalnych. Podczas typowych operacji edycji, kiedy programiści pracują (zazwyczaj) przy oddzielnych modułach projektu, przeważnie zadowalające są funkcje oferowane przez środowiska programistyczne w połączeniu ze specjalnym systemem zarządzania konfiguracją (ang. Software Configuration Management, SCM) [6, 30, 53, 75] takim, jak np. CVS [11], Subversion [77] czy Git [32]. Niestety podczas refaktoryzacji – z uwagi na fakt, iż pojedyncza operacja może wprowadzać zmiany w bardzo wielu powiązanych ze sobą fragmentach kodu (odpowiednio modelu) rozrzuconych pośród wielu modułów projektu [27] – takie środowisko okazuje się nie spełniać oczekiwań [15, 55]. Źródłem problemów jest m.in. fakt, iż dostępne systemy zarządzania konfiguracją stawiając na uniwersalność, wykorzystują wyłącznie techniki łączenia tekstów przez co są niezależne od języka programowania, lecz nie są w stanie wykorzystać żadnych informacji o przekształcanej strukturze. Środowiska programistyczne coraz częściej posiadają dużo szersze możliwości dzięki temu, że operują na wyższym poziomie abstrakcji a mianowicie na drzewie składni (ang. abstract syntax tree – AST) [29, 34, 64]. Wciąż jednak nie rozpoczęto prac nad możliwościami współpracy pomiędzy rozproszonymi środowiskami na tym poziomie.. 10.

(11) Jak wykazał Mens [55, 57, 58] już przy najprostszych przekształceniach refaktoryzujących wprowadzanych równolegle przez dwóch użytkowników (np. przesunięcie zmiennej składowej między klasami i jednoczesna zmiana jej nazwy przez innego użytkownika) po scaleniu techniką łączenia tekstów pojawiają się poważne problemy (zwane potocznie konfliktami). W najlepszym wypadku użytkownik dostaje sygnał o licznych, niezrozumiałych dla środowiska błędach [15, 55]. Prawidłowe łączenie zmian zależne jest bowiem od syntaktyki kodu (odpowiednio modelu) oraz od semantyki operacji [53], przez co fragmenty scalone po refaktoryzacji mogą prowadzić do błędów kompilacji lub do niepoprawnych, choć kompilowalnych, programów (jak np. podczas niezamierzonego nadpisania metody z klasy nadrzędnej) [57]. Takie niebezpieczeństwa zmuszają członków zespołu do wzmożonego zaangażowania podczas procesu łączenia zmian – trzeba analizować wszystkie wprowadzone operacje i manualnie eliminować niezgodności – jest to proces złożony i czasochłonny. Z tego względu w praktyce codziennej bardzo często w sposób sztuczny ogranicza się możliwość przeprowadzania refaktoryzacji przez członków zespołu lub wręcz jej zabrania. Zwykle prowadzi to do wykonywania refaktoryzacji w sekcji krytycznej, czyli umożliwienia jej realizacji co najwyżej jednemu projektantowi w danym momencie czasu. Komfort pracy w takich warunkach jest wątpliwy; zachodzi również sprzeczności takiego podejścia z paradygmatem programowania zwinnego, które uznaje wszystkich członków zespołu za sobie równych, a równoległą refaktoryzację za kluczową technikę.. 1.3 Cel badań Celem nadrzędnym rozprawy była analiza powstających konfliktów podczas pracy grupowej oraz wskazanie metod ich eliminacji. W rozprawie formułuje się następującą tezę: Możliwe. jest. opracowanie. metod. i. systemu. dla. automatycznego. wspomagania refaktoryzacji, który: . umożliwi równoległą pracę zespołów projektowych w środowisku rozproszonym (przy częściowej replikacji struktur danych) i wykryje błędy wynikające z równoległego zlecania refaktoryzacji;. 11.

(12) . automatycznie wyeliminuje skutki konfliktów, w przypadku równoległego wykonywania elementarnych refaktoryzacji;. . umożliwi łączenie elementarnych operacji w sekwencje i wprowadzanie ich w sposób transakcyjny oraz zminimalizuje nakład pracy projektantów, w przypadku rozwiązywania konfliktów spowodowanych równoległym wykonaniem różnych sekwencji refaktoryzacji.. 1.4 Organizacja rozprawy Rozprawa została podzielona na siedem rozdziałów. W rozdziale pierwszym zamieszczono wstęp do tematyki oraz uzasadniono jej ważność. Przedstawiony został cel badań. Rozdział drugi przedstawia formalne definicje refaktoryzacji, klasyfikację oraz przebieg procesu przeprowadzania. Następnie wprowadza opis wzajemnych zależności między refaktoryzacjami, pojęcia kompozycji oraz łańcucha refaktoryzacji. Rozdział kończy przegląd aktualnych wyników prac i wskazanie ich braków. W rozdziale trzecim zebrane zostały podstawowe wymagania wobec tworzonego środowiska pracy zespołowej. Wprowadzono koncepcję repozytorium grafowego, na której oparty zostanie budowany system. Przeanalizowane zostały tryby pracy z ciągłym dostępem do węzła globalnego oraz w przypadku celowego odłączenia na czas zmian i eksperymentów. Rozdział zawiera również prezentację formalizmu transformacji grafowych dla formalnego opisu systemu refaktoryzacji, wspomagającego mechanizm kontroli produkcji gramatyki za pomocą diagramów sterujących wywodem. W rozdziale czwartym, stosując atrybutowane grafy z jednoznacznym indeksowaniem, wykazano, że reprezentacja ta pozwala na efektywną identyfikację komponentów modelu oprogramowania na poziomie równoważnym diagramom klas UML. Do opisu ewolucji oprogramowania (w tym refaktoryzacji) zbudowana została dedykowana gramatyka grafowa. W rozdziale piątym dla repozytorium grafowego opartego na grafach z indeksowaniem zaprezentowano metody eliminacji konfliktów występujących przy równoległym wykonaniu elementarnych refaktoryzacji. Wykazano poprawność 12.

(13) metody oraz pokazano rozwiązanie wybranych problemów reprezentatywnych. Przeanalizowano również kluczowe czynniki powodujące powstawanie konfliktów. Na tej podstawie zasugerowano dalsze mechanizmy potrzebne w systemie, które pozwolą na wsparcie eliminacji i omijania kolejnych konfliktów. W rozdziale szóstym, zdefiniowano koncepcję łańcuchów refaktoryzacji jako reprezentacji. przygotowania. złożonych. operacji. refaktoryzacji. (będących. sekwencjami operacji elementarnych) przez kilka zespołów pracujących współbieżnie. Na tej bazie opracowano algorytm scalania tych refaktoryzacji, który minimalizuje czas poświęcony przez zespoły do otrzymania systemu wolnego od kolizji. Rozdział siódmy zawiera podsumowanie wyników prac i wskazanie dalszych kierunków rozwoju.. 13.

(14) Rozdział 2 Refaktoryzacja w środowisku rozproszonym W niniejszym rozdziale przytoczone zostaną formalne definicje refaktoryzacji oraz związków i zależności między refaktoryzacjami. Omówiony zostanie przypadek pracy w rozproszonym środowisku wieloużytkownikowym oraz kluczowe dla rozprawy pojęcie konfliktu przekształceń. Przeanalizowane zostaną wyniki dotychczas prowadzonych badań w zakresie poszukiwania rozwiązań i sposobów eliminacji konfliktów. W szczególności wskazane będą braki oraz niedogodności dostępnych rozwiązań, z uwagi na fakt, że propozycja ich udoskonalenia będzie przedmiotem niniejszej pracy.. 2.1 Pojęcie refaktoryzacji Pod pojęciem refaktoryzacji rozumieć będziemy takie przekształcenie programu obiektowo zorientowanego, które nie zmieni jego obserwowalnego zachowania [27, 42, 62, 63], ale mimo to w sposób znaczący wpłynie na poprawę jakości projektu. Pierwsza uproszczona definicja refaktoryzacji pojawiła się w 1991r. w pracach profesora Ralpha Johnsona i jego doktoranta Williama Opdake'a [62]. Określenie własności poprawy struktury programu dodał dopiero Fowler [27], nie wskazując jednak sposobu jej miary. Podkreśla on, iż z definicji celem refaktoryzacji jest uczynienie oprogramowania czytelniejszym oraz łatwiejszym do dalszych modyfikacji (w etapie późniejszej pielęgnacji). Z kolei Don Roberts w swojej rozprawie doktorskiej [71], z uwagi na trudność ujęcia formalnego niezmienności działania programu (ang. behaviour preservation), zaproponował rezygnację z takiego określenia na rzecz warunków wstępnych (ang. preconditions), notabene wprowadzonych już przez Opdake'a [63]. Warunki wstępne muszą zachodzić aby 14.

(15) przekształcenie mogło zostać przeprowadzona poprawnie, bez uszczerbku dla funkcjonalności programu. Roberts [71] dołożył także warunki końcowe (ang. postconditions), które określają jak zmodyfikowane zostaną warunki wstępne po wykonaniu refaktoryzacji. Opdake [63] sugerował posługiwanie się niezmiennikami (ang. invariants), aby w ten sposób kontrolować niezmienność zachowania. Oto definicje wprowadzone przez Roberts'a [71]:. DEFINICJA 2.1 Refaktoryzacja to para R = (pre, T), gdzie: . pre jest warunkiem (wstępnym) jaki powinien spełniać program aby wykonanie przekształcenia R było poprawne;. . T jest transformacją programu.. DEFINICJA 2.2 Refaktoryzacja jest uporządkowaną trójką R = (pre, T, P), gdzie: . pre jest asercją określającą poprawność zastosowania R dla programu;. . T jest transformacją tego programu,. . P to funkcja odwzorowującą warunki wejściowe na warunki końcowe, przy zastosowaniu transformacji T.. Definicje powyższe nie określają jak formalnie zapisane będą warunki wstępne i jak reprezentowana transformacja programu. Roberts stosuje w tym celu logikę predykatów pierwszego rzędu, proste funkcje analityczne [71], takie jak np.: IsClass(),. IsGlobal(),. ClassReferences(),. Method(),. Superclass();. oraz. bardziej. Superclasses*(),. rozbudowane. jak:. IsEmptyClass(),. Subclasses(),. Subclasses*(),. LookedUpMethod(),. IsGetter(),. IsSetter(), DefinesInstanceVaribles(); gdzie gwiazdka oznacza wszystkich. przodków lub potomków w hierarchii. Rozszerzona definicja (definicja 2.2) uwzględnia warunki końcowe, jakie będą spełnione po wykonaniu przekształcenia refaktoryzującego, co okazuje się użyteczne podczas analizy poprawności sekwencji przekształceń refaktoryzujących [71] jak również w przypadku analizy zależności między przekształceniami. 15.

(16) Poszczególne z operacji refaktoryzacji przedstawia się szczegółowo poprzez określenie [27]: . celu,. . argumentów,. . opisu,. . warunków stosowalności,. . stanu wejściowy,. . stanu końcowego.. Fowler [27] dodaje ponadto motywację stosowania oraz tzw. mechanikę czyli swego rodzaju algorytm opisującym jak krok po kroku wprowadzać refaktoryzację w kodzie (poprzez edycję). Przykładami najprostszych refaktoryzacji są: zmiana nazwy metody (ang. Rename Method), zmiana nazwy zmiennej (ang. Rename Variable), przesunięcie metody (ang. Move Method) lub zmiennej (ang. Move Variable) do innej klasy. Listę oraz opis wybranych refaktoryzacji zamieszczono w Dodatku A. Refaktoryzacje zwykło się przedstawiać w uproszczony sposób za pomocą diagramów UML [28, 61] obrazujących dwa fragmenty projektu – przed i po transformacji (jak na rysunku 2.1). Warunki stosowalności można dodatkowo ująć wpisując je pod strzałką transformacji; dla przykładu refaktoryzację przesunięcia metody między klasami można zastosować gdy metoda o określonej nazwie („m”) nie została jeszcze zadeklarowana w klasie docelowej (tutaj „D”), ani też w żadnej z klas należących do jej hierarchii.. 16.

(17) Rysunek 2.1. Reprezentacja refaktoryzacji przesunięcia metody o nazwie „m” z klasy o nazwie „C” do klasy „D”.. 2.2 Klasyfikacja refaktoryzacji Katalog przekształceń refaktoryzujących zaproponowany przez Fowlera [27] obejmuje 72 przekształcenia. Wiele innych refaktoryzacji zaproponowano w literaturze później [41]. Spośród skatalogowanych około 22 to przekształcenia analitycznie proste, których poprawność można udowodnić w oparciu o kod bez uruchamiania programu [83] a pozostałe to tzw. trudne, które w celu udowodnienia poprawności wymagają np. testów jednostkowych. Połowa spośród operacji trudnych może być jednak testowana w sposób szablonowy [83], pozostałe wymagają znacznie większego nakładu pracy – poprzez sprawdzanie asercji możemy jedynie przybliżyć się do pełnej weryfikacji poprawności. Refaktoryzacje klasyfikuje się ponadto na kilka innych sposobów [16, 42]. Można rozważać refaktoryzacje dla języków innych niż obiektowe (np. dla języków funkcyjnych lub logicznych) czy też refaktoryzacje w systemach bazodanowych [69]. Opdake [63] wprowadził podział na refaktoryzacje proste i złożone, a więc takie, które są niepodzielne oraz takie, które składają się z tych pierwszych. Zakłada się, iż istnieje skończony zbiór refaktoryzacji niskiego poziomu. Refaktoryzacje złożone to ciągi refaktoryzacji prostych mające określony cel zmiany architektonicznej projektu; nazywane są one również w literaturze kompozycjami refaktoryzacji. Roberts wyróżnia z kolei: refaktoryzacje operujące na poziomie klas, metod oraz zmiennych. Norda [60] wprowadza zbliżony podział, według obszaru działania, na: proceduralne (wewnątrz pojedynczej metody), obiektowe (zmieniają strukturę obiektową programu) i pojęciowe (zmieniają strukturę logiczną np. wprowadzające wzorce projektowe [6, 29, 31]). 17.

(18) Refaktoryzacje dzieli się również w zależności od stopnia automatyzacji ich wprowadzania: od wykonania manualnego, poprzez wspomagane automatycznie do w pełni zautomatyzowanych [42]. W przypadku refaktoryzacji wspomaganych automatycznie użytkownik decyduje kiedy i jakie refaktoryzacje wykonać. Przy pełnej automatyzacji kontrolę powinno przejąć inteligentne narzędzie i samo wykrywać potrzebę oraz rodzaj refaktoryzacji do zastosowania. Refaktoryzację manualną przeprowadza natomiast użytkownik samodzielnie poprzez proces refaktoryzacji.. 2.3 Proces przeprowadzania refaktoryzacji Proces refaktoryzacji składa się z kilku powtarzalnych kroków [27, 56]:. 1. Zidentyfikowanie części programu wymagającej poprawy. 2. Wybór odpowiedniego przekształcenia refaktoryzującego. 3. Sprawdzenie czy wybrana operacja nie modyfikuje działania programu. 4. Zastosowanie refaktoryzacji (zgodnie z jej mechaniką). 5. Ocena rezultatów; pomiar jakości programu i wpływu na produktywność. 6. Zapewnienie zgodności programu z innymi artefaktami (specyfikacją, projektem, modelem, testami).. Kwestia identyfikowania części programu i wyboru refaktoryzacji wiąże się z odpowiedzią na pytanie co i kiedy refaktoryzować. Jest to proces wykrywania tzw. brzydkich zapachów (ang. bad smells) [27], czyli fragmentów które wręcz same sugerują potrzebę modyfikacji konstrukcji; mogą one dotyczyć: występowania powielonego kodu, zbyt długich metod, zbyt dużych klasa, długich list parametrów, zbyt obszernych funkcjonalność klas, nadmiernego poszatkowania (zmuszającego do wykonywania wielu drobnych modyfikacji w rożnych miejscach), złego pogrupowania danych i operacji, równoległych hierarchii dziedziczenia, pól używanych tymczasowo, łańcuchów wywołań, klas bibliotecznych niespełniających oczekiwań bibliotecznych, a nawet miejsc zbyt licznych komentarzy (co sugeruje, iż kod został źle podzielony lub. 18.

(19) metody mają niewłaściwie dobrane nazwy). Opis zaleceń oraz takich konstrukcji zamieszcza m.in. Fowler [27]. Niestety jasne kryteria rozpoznawania nie są możliwe do sprecyzowania; żaden zestaw reguł nie dorówna bowiem intuicji projektanta popartej wiedzą i doświadczaniem. Znaleźć można natomiast liczne wskazówki jak poszukiwać brzydkich zapachów oraz zalecenia, które refaktoryzacje stosować przy jakich problemach. Fowler identyfikuje nawet przypadek, kiedy nie powinno się refaktoryzować – gdy kod jest na tyle zły, że bardziej opłaca się stworzyć projekt od nowa. Wyjściem z takich sytuacji może być też podział kodu na mniejsze części i wtedy podejmowanie decyzji o dalszej refaktoryzacji bądź ponownej implementacji. Przykłady przeprowadzania procesu refaktoryzacji prezentują m.in. prace [12, 13] oparte na przykładzie symulacji lokalnej sieci komputerowej (ang. LAN). Przykład ten jest wykorzystywany i rozszerzany przez wiele ośrodków do nauczania refaktoryzacji a także w celach badawczych. Inny przykład będący modelem systemu wydruków znaleźć można w pracy [56].. 2.4 Środowisko pracy zespołowej Podstawową motywacją w prowadzonych badaniach była obserwacja, iż oprogramowanie jest tworzone i rozwijane przez zespoły ludzi, które najczęściej są rozmieszczone w różnych miejscach na świecie [4, 51, 53, 75]. Projektanci i programiści pracują na własnej kopii oprogramowania (kodu/modelu) w swoim lokalnym środowisku często nie tylko bez wiedzy o modyfikacjach wprowadzanych przez innych ale także o samych członkach zespołu. Jak wskazuje [65] fundamentalną kwestią podczas budowy i rozbudowy złożonych systemów dużej skali jest zarządzanie wprowadzaniem równoległych zmian oraz wsparcie ludzi przeprowadzających te zmiany poprzez struktury organizacyjne, zarządzanie projektem, proces i technologię. Trzeba mierzyć się z takimi problemami jak rozproszenie wiedzy oraz ewolucja oprogramowania. Możliwości i sposoby wsparcia procesu tworzenia oprogramowania mają ogromny wpływ m.in. na jakość i terminowość realizacji produktów. Dziedzina zajmująca się gromadzeniem wiedzy i ewolucją oprogramowania w przypadku systemów dużej skali to zarządzenie konfiguracją (ang. Software 19.

(20) Configuration Management – SCM) [6, 30, 56, 75]. Rozróżnia ona dwa zasadnicze modele pracy: . model pesymistyczny,. . model optymistyczny.. W modelu pesymistycznym, równoległa edycja tego samego pliku jest zabroniona co realizowane jest poprzez fizyczne zablokowanie dostępu. W praktyce rozwiązanie takie okazuje się niewystarczające i nie wchodzi w rachubę już w przypadku kilkuosobowego zespołu [15, 56] – mamy tu bowiem do czynienia z nieefektywnym oczekiwaniem przez kolejnych członków zespołu na decyzje innego i odblokowanie przez niego zajmowanego zasobu. W modelu optymistycznym wszyscy członkowie mają możliwość całkowicie niezależnej pracy (na pozyskanej wcześniej lokalnej kopii). Zyskują swobodę wprowadzania zmian w wielu fragmentach projektu bez konieczności zwracania jakiejkolwiek uwagi na to, co aktualnie modyfikują inne osoby z zespołu. Po zakończeniu etapu prac użytkownicy odsyłają rezultaty do systemu (a dokładnie tzw. głównego repozytorium zlokalizowanego w węźle centralnym) w celu zbudowania nowej, globalnej wersji. Niestety ceną opisanej elastyczności jest konieczność łączenia zmian przed ich zatwierdzeniem w repozytorium, co często prowadzi do dodatkowych problemów, które potocznie określane są mianem konfliktów. Sytuacja taka wymaga wypracowania metod efektywnego wykrywania konfliktów a także, z uwagi, że ich eliminacja narzuca konieczność dodatkowego nakładu pracy członków zespołu, metod efektywnego scalania fragmentów będących przyczyną problemów.. 2.4.1 Łączenie zmian po refaktoryzacji Ryzyko nakładania się zmian i związana z tym potrzeba odpowiedniego łączenia. tak. powstałych. błędnych. konstrukcji. rośnie. szczególnie. podczas. wykonywania refaktoryzacji, a to za sprawą jej nieograniczonego zasięgu. Istniejące systemy kategorii SCM (jak CVS [11], czy Subversion [77]) replikują kody źródłowe jako pliki tekstowe – każdy zasób, w szczególności kod lub reprezentacja modelu, jest taktowany jako ciąg linii. Dzięki temu zyskują elastyczność i niezależność od języka programowania. Jeżeli lokalne zmiany użytkownika oraz wprowadzone w międzyczasie przez innego użytkownika zmiany globalne dotyczą 20.

(21) rozłącznych obszarów, nic nie stoi na przeszkodzie, aby je automatycznie scalić – w wyniku powstaje nowy plik, który zawiera wszystkie poprawie zmodyfikowane linie. Sytuacja komplikuje się już, kiedy zmienione lokalne i zdalne linie nakładają się. Systemy SCM wykorzystują tu na ogół algorytm 3-kierunkowego łączenia zmian (ang. 3-way merging) [53] na podstawie stanu lub zapisu różnic; w przeciwieństwie do 2kierunkowego łączenia (ang. 2-way merging) [53], zyskując możliwość wglądu nie tylko w łączone pliki, ale także w ich wspólny stan poprzedni. Innym podejściem jest łącznie zmian na zasadzie zgrywania i późniejszego odtwarzania kroków edytorskich; metodę tę stosuje z większym powodzeniem m.in. bardzo popularny w ostatnich latach system Git [32]. W obu rozwiązaniach ze względu na nieznajomość syntaktyki oraz semantyki operacji wysokiego poziomu (jak refaktoryzacja) może dojść do niebezpiecznych i trudno lokalizowanych błędów (takich jak np. przypadkowe przesłonięcie metody w hierarchii klasy) [15]. System nie jest w stanie automatycznie połączyć zmian i zmuszony jest prosić użytkownika o pomoc. Wiele poważnych problemów pojawić się zatem może, kiedy dwóch projektantów spróbuje zastosować refaktoryzację na tym samym fragmencie oprogramowania [48, 55, 57]. Istotą jest nieznajomość semantyki oraz sam fakt, iż pojedyncza operacja refaktoryzacji, w przeciwieństwie do typowej edycji, może wprowadzać zmiany w bardzo wielu fragmentach rozrzuconych po całym systemie, pośród wielu pakietów i klas, zwiększając prawdopodobieństwo ich nakładania się. Rozpatrzmy dla przykładu sytuację, kiedy dwóch projektantów decyduje się wprowadzić zmiany obejmujące tą samą metodę w tej samej klasie. Jeden z nich dokonuje zmiany nazwy metody (Rename Method), natomiast drugi przesuwa tą metodę z jej aktualnej klasy do innej (Move Method z klasy o nazwie „C” do klasy o nazwie „D”) – rysunek 2.2 obrazuje ją za pomocą diagramów UML. Następnie, najprawdopodobniej po serii innych modyfikacji, programiści decydują się na współdzielenie swoich wersji z resztą zespołu. Drugi z nich, biorąc pod uwagę kolejność synchronizacji z repozytorium, napotka problemy podczas zatwierdzania zmian – konflikty pojawią się nie tylko w dwóch wspomnianych klasach, ale również we wszystkich miejscach gdzie metoda była wywoływana – porównywane tekstowo linie kodu będą niezgodne z intencją projektantów (będą zawierać inną nazwę).. 21.

(22) Rysunek 2.2. Konflikt łączenia zmian podczas przenoszenia metody oraz równoległej zmiany nazwy tej samej metody prowadzonych przez dwóch użytkowników.. Stosując. dowolny. z. obecnie. istniejących. komercyjnych. systemów. wspierających refaktoryzację czy środowisko programistyczne (np. Eclipse [18]) z systemem kontroli wersji nie mamy możliwości wcześniejszego wykrycia wielu podobnych, do wspomnianych powyżej, sytuacji. Tym samym nie mamy żadnego wsparcia w eliminacji konfliktów. Jeżeli w opisany wyżej przykładzie zdamy się na automatyczne łączenie zmian, możemy stracić rezultat jednego z przekształceń bądź uzyskamy nieprzewidywalne konstrukcje jak na przykład dwie „podobne” metody. W konsekwencji, dość często w takich przypadkach, projektant zmuszony jest bardzo wnikliwie analizować jakie zmiany zostały wcześniej wprowadzone przez innych członków zespołu i zdecydować, które operacje powinny być zaakceptowana, a które odrzucone.. Jak. można. się. spodziewać. często. takie. decyzje. wymagają. przedyskutowania w zespole. Podsumowując proces łączenia zmian bez znajomość semantyki operacji i syntaktyki języka (lub modelu) pozostaje skomplikowany i może prowadzić do poważnych błędów. Błędy takie wykrywane są dość późno, bowiem dopiero kiedy ostatni członek zespołu udostępnia swoje modyfikacje zmuszając przy tym również pozostałe osoby do dodatkowej czujności podczas tego procesu. Sytuacja komplikuje się jeszcze bardziej, kiedy wprowadzanych zmian jest setki. W wielu przypadkach, jak. 22.

(23) pokazuje praktyka, może okazać się nawet konieczna rezygnacja lub ponowne wykonanie niektórych refaktoryzacji.. 2.4.2 Kompozycje oraz łańcuchy refaktoryzacji Z obserwacji praktycznych wynika, iż refaktoryzacje rzadko wprowadzane są pojedynczo. Kiedy programista zmienia strukturę kodu, ma najczęściej w zamyśle pewien cel (np. wydzielić cześć wspólna dwóch metod i przenieść ją do klasy nadrzędnej), a następnie wprowadzając serię przekształceń stara się go osiągnąć. Refaktoryzacje występować będą zatem najczęściej w sekwencjach. Ciąg operacji mający na celu wprowadzenie poważniejszej zmiany koncepcji architektonicznej nazwiemy kompozycją refaktoryzacji (ang. composite refactoring) [71]; kompozycje refaktoryzacji. są. powszechnie. stosowane. do. wprowadzaniu. konstrukcji. odpowiadających wzorcom projektowym [6, 31, 41]. Z kolei dowolną sekwencję poprawnych refaktoryzacji nie koniecznie powiązanych wspólnym celem nazwiemy łańcuchem refaktoryzacji (ang. refactoring chain). Za Roberts'em [71] formalnie pojęcie to zdefiniujemy następująco:. DEFINICJA 2.3 Łańcuch refaktoryzacji to sekwencja poprawnych refaktoryzacji <R1, R2, ..., Rn> zastosowanych do programu w kolejności 1 do n, przy założeniu, że wszystkie z nich mogą zostać wykonane.. Dowolna sekwencja poprawnych refaktoryzacji, jako ciąg operacji nie zmieniających obserwowalnego zachowania programu, w oczywisty sposób także stanowi refaktoryzację (złożona operacja nadal nie narusza zachowania programu). Poprawność operacji w sekwencji należy rozumieć jednoznaczne ze spełnienie odpowiednich warunków wstępnych dla każdej kolejno wprowadzanej operacji – R1 do Rn. Nie wszystkie warunki muszą być spełnione na starcie, gdyż jedne operacje mogą przygotowywać warunki do wprowadzenia innych (powiemy dalej, że refaktoryzacja Rb zależy od Ra); dla każdego k ϵ (1 ... n-1) po zastosowaniu sekwencji R1 ... Rk powinny być spełnione warunki wstępne dla refaktoryzacji Rk+1 (jednak nie koniecznie dla Rk+2 ... Rn). 23.

(24) Niepodzielność (o której mowa w definicji 2.3), podczas wprowadzania łańcucha w środowisku lokalnym, w najprostszy sposób można zapewnić zakazując stosowania jakichkolwiek innych operacji w trakcie przeprowadzania sekwencji przekształceń. Należy również zauważyć, iż zbiór wspólnych warunków wstępnych dla sekwencji. nie. jest. prostą. koniunkcją. poszczególnych. spośród. warunków. refaktoryzacji składowych; minimalny zbiór warunków wymagających sprawdzenia przed zastosowaniem całego łańcuch będzie zawężony. Wyznaczeniem takich warunków dla łańcuchów refaktoryzacji zajmował się w swej rozprawie Roberts [71]. W łańcuchu refaktoryzacji możemy wyodrębnić pod-łańcuchy (lub też kompozycje składowe), należy jednak podkreślić, iż nie każdy podciąg jest łańcuchem, gdyż może nie stanowić poprawnej sekwencją operacji.. DEFINICJA 2.4 Łańcuch Sb = <RB1, ..., RBm> jest pod-łańcuchem łańcucha Sa = <RA1, ..., RAn>, co zapiszemy Sb  Sa, jeżeli ciąg indeksów B1 ... Bm jest podciągiem ciągu A1 … An.. Dla uproszczenia zapisu łączenia łańcuchów będziemy stosować następujące działanie konkatenacji ('+'): <RA1, ..., RAm> + <RB1, ..., RBm> = <RA1, ..., RAm ,RB1, ..., Rbm>.. 2.4.3 Związki między refaktoryzacjami W oparciu o definicje Roberts'a [71] można sformułować następujące pojęcia zamienności oraz zależności refaktoryzacji.. DEFINICJA 2.5 Refaktoryzacje Ra = (prea, Ta, Pa) i Rb = (preb, Tb, Pb) są zamienne (ang. commute) dla programu Q co zapisujemy Ra  Rb wtedy i tylko wtedy gdy <Ra, Rb> oraz <Rb, Ra> są łańcuchami dla programu Q oraz funkcje analityczne po zastosowaniu obu z łańcuchów są jednakowe Pa(Pb(Q)) = Pb(Pa(Q)).. 24.

(25) W praktyce, o ile środowisko refaktoryzacyjne pozwala, obie refaktoryzacje zamienne można przeprowadzić w dowolnej kolejności. Refaktoryzacje nie będą zamiennymi jeżeli wykluczają się wzajemnie lub jedna ustawia warunki wstępne dla drugiej. W drugim przypadku refaktoryzacje są zależne – relacja zależności określa, iż przeprowadzenie refaktoryzacji może nastąpić dopiero po wykonaniu innej.. DEFINICJA 2.6 Mając dany łańcuch <Ra, Rb> powiemy ze Rb jest zależna (ang. depends) od Ra co zapisywać będziemy Ra < Rb gdy refaktoryzacje Ra i Rb nie są zamienne. Refaktoryzacja Rb staje się zatem poprawna dla programu dopiero po tym jak zastosowana zostanie Ra. Wykonanie refaktoryzacji w odwrotnej kolejności nie jest możliwe – <Rb, Ra> nie stanowi łańcucha lub wynik jego zastosowania jest inny niż w przypadku aplikacji <Ra, Rb>. Relacja zależności, przy założeniu że każda refaktoryzacja jest zależna od samej siebie (a Ra < Ra), jest relacją częściowego porządku [71]. Relacja ta wprowadza też podział łańcucha refaktoryzacji na rozłączne pod-łańcuchy, które można wykonywać niezależnie (np. równolegle) – szkic algorytmu wydzielenia podłańcuchów zaprezentował w swej rozprawie Roberts [71]. Należy zauważyć, iż obie relacje zamienności i zgodności mogą nie zachodzić w praktyce z uwagi na ograniczenia rzeczywistego środowiska programistycznego; relacje mogą być słabsze lub może zachodzić konflikt podczas wprowadzania refaktoryzacji. Dla kompletnego ujęcia związków między refaktoryzacjami oraz z uwagi na użyteczność sprecyzujmy jeszcze formalnie definicję odwrotności refaktoryzacji.. DEFINICJA 2.7 Powiemy że refaktoryzacja Rb = (preb, Tb, Pb) jest odwrotną do Ra = (prea, Ta, Pa) dla programu Q, co zapisujemy Rb = Ra-1, gdy Pa(Pb(Q))) = Pb(Pa(Q)) = Q oraz Tb = Ta-1. Ponieważ refaktoryzacja z definicji nie zmienia działania programu to operacja do niej odwrotna również zachowuje ten warunek, a więc będzie refaktoryzacją. 25.

(26) 2.5 Stan dotychczasowych badań Problem łączenia zmian po refaktoryzacji oraz nasilającego się występowania konfliktów podczas tego procesu jest przedmiotem coraz intensywniejszych badań naukowych. Poczynając od pracy Robertsa [71], wprowadzającej formalnie zależności między refaktoryzacjami i wskazującej ich możliwe zastosowania (również, w interesującym nas, zakresie wsparcia programistów w unikaniu niezgodności podczas wspólnej pracy nad projektem) badania w tej dziedzinie rozpoczęły także inne zespoły. W mojej ocenie, najbardziej znaczące wyniki uzyskała grupa Mens'a [55, 57, 58, 66]. Zespół ten pierwszy rozwinął w literaturze problem niezgodności refaktoryzacji w rzeczywistych środowiskach programistycznych, opisał możliwe konflikty między parami operacji wprowadzanymi sekwencyjnie oraz zaproponował podejście do ich rozwiązania. Formalnie w celu identyfikacji potencjalnych konfliktów zastosowano analizę par krytycznych (ang. critical pairs) [57] opartą o teorię grafów. Wynik prac stanowi klasyfikacja zebrana w formie tabeli (rysunek 2.3); komórka tabeli reprezentuje liczbę potencjalnie możliwych konfliktów między parą przekształceń; dla przykładowej pary Rename Variable i Move Variable (komórka 10:1) potencjalnie wystąpić mogą dwa konflikty, z których jeden to próba zmiany nazwy zmiennej, która nie jest już dostępna (gdyż został przeniesiona), a drugi to próba przeniesienie zmiennej, która formalnie „przestała istnieć” (ponieważ zmieniono jej nazwę). Klasyfikacja ta wprowadzona była pierwotnie [55] w sposób całkowicie statyczny celem analizy zadanych ciągów refaktoryzacji i próby wyboru najlepszego podzbioru refaktoryzacji spośród potencjalnie możliwych do zastosowania; w latach kolejnych została rozszerzona o możliwość dostosowania dla konkretnego programu.. 26.

(27) Rysunek 2.3. Tabela potencjalnych konfliktów refaktoryzacji opracowana przez Mens'a w oparciu o analizę par krytycznych. Mens koncentruje się najpierw na środowisku jednostanowiskowym. Rozważa dalej środowisko rozproszone [57] jednak w sposób nieformalny oraz dla przypadku konfliktu wyłącznie pojedynczo wprowadzanych refaktoryzacji. Podejście to nie uwzględnienia. interakcji. w. rzeczywistym,. rozproszonym. środowisku. wieloużytkownikowym, w którym projektanci mogą wprowadzać całe łańcuchy operacji w sposób dynamiczny (online). Zarówno w teorii jak i w praktyce można uznać, że sekwencyjne wprowadzanie refaktoryzacji w środowisku lokalnym nie stanowi już problemu. W przypadku. wieloużytkownikowego. środowiska. rozproszonego. sytuacja. się. komplikuje: . równolegle wprowadzanie łańcuchów powoduje wzrost prawdopodobieństwa występowania konfliktów,. . koszt konfliktu (tym samym koszt refaktoryzacji) jest większy ze względu na możliwość utraty efektów długotrwałej pracy zespołu.. Problemem konfliktów podczas łączenia zmian po zastosowaniu refaktoryzacji w sposób praktyczny, równolegle do prowadzonych przeze mnie prac, zajmował się 27.

(28) również Danny Dig [15] z grupy profesora Ralpha Johnsona [31]. Zauważa on, iż nadanie klasom i składnikom identyfikatorów pomaga w wykryciu i dalszym rozwiązaniu wielu spośród problemów1 [15]. Bazując na tym spostrzeżeniu zbudował narzędzie o nazwie MolhadoRef [15] stanowiące dodatek do środowiska Eclipse [18]. Narzędzie to emuluje identyfikację komponentów opierając swe działanie na sztucznym wycofywaniu refaktoryzacji poprzez wykonanie przekształceń odwrotnych w środowisku lokalnym, a następnie ponowne odtwarzanie, podczas łączenia zmian w węźle centralnym. Autorzy stosują również pewne triki (np. sztuczne nazwy) pozwalające wyeliminować konflikty innego rodzaju. Podejście to jest bardzo praktyczne i zostało zauważone przez środowisko komercyjne, co dodatkowo utwierdza mnie w przekonaniu o celowości prowadzonych prac. Niestety sztuczna implementacja identyfikacji komponentów nie jest ciekawym i kompleksowym rozwiązaniem problemu w środowisku rozproszonym. Brak także właściwego ujęcia formalnego. Założenie o wykonywaniu refaktoryzacji odwrotnych i stosowaniu ich ponownie jest nieefektywne. Moment rozpoznania sytuacji konfliktowej jest zbyt późny, ma bowiem miejsce dopiero kiedy ostatni projektant gotów jest do synchronizacji i zatwierdzenia zmian do węzła centralnego (repozytorium). Każdy z zespołów badawczych inaczej formułuje pojęcie konfliktu, żaden nie definiuje go w ramach serii refaktoryzacji. Tymczasem takie ujęcie formalne, z uwzględnieniem możliwości wykorzystania wiedzy o semantyce refaktoryzacji, jest o tyle istotne, iż pozwoli na właściwy podgląd na ewolucję oprogramowania, która zaburzana. jest. przecież. szczególnie. po. wprowadzaniu. refaktoryzacji. [15]. (skutkujących najczęściej wieloma drobnymi i porozrzucanych zmianami w kodzie). Przejście na wyższy poziom abstrakcji zapewni również podgląd historii zmian w projekcie na poziomie bardziej właściwym niż elementarne operacje edycji (z którymi mamy do czynienia w przypadku obróbki kodu programów jako tekstu w aktualnie dostępnych. na. rynku. środowiskach. programistycznych).. Wprowadzenie. mechanizmów pozwalających kontrolować i planować refaktoryzacje pozwoli z kolei zmierzyć się z problemem wprowadzania refaktoryzacji dużej skali (ang. large refactoring) [69] przeprowadzanych krok za krokiem nawet przez wiele dni. 1. Podejście to stosowane było wcześniej przez autora pracy [48, 49] z tym, że identyfikatory są powiązane w sposób formalny ze strkturami grafowymi wspierającymi poprawne rozwiązanie.. 28.

(29) Rozdział 3 Koncepcja systemu kontroli refaktoryzacji W celu wspomagania refaktoryzacji prowadzonej w środowisku rozproszonym [80] przez zespoły projektantów-programistów zaprojektowany został specjalny system typu Collaborative Software Environment [4, 65, 82]. Będzie on dalej w pracy nazywany Distributed Refactorization Environment lub w skrócie DRE. Na początku rozdziału przedstawione zostaną podstawowe założenia oraz wymagane własności systemu. Następnie uzasadniony zostanie wybór, przyjętego w pracy, grafowego modelu reprezentacji oprogramowania oraz wprowadzona koncepcja repozytorium grafowego jako sposobu realizacji jądra modelowanego środowiska. Zaprezentowany zostanie formalizm gramatyk i transformacji grafowych w podejściu algebraicznym, które stanowić będą właściwą i jedyną możliwość operacji na repozytorium grafowym. W dalszej części przedstawiona będzie architektura budowanego systemu DRE. Omówiony zostanie przebieg wykonania refaktoryzacji oraz mechanizmy i elementy składowe wpływające na użyteczność środowisk podczas koordynacji prac przy dostępie współbieżnym. Na koniec rozważone zostaną dopuszczalne tryby pracy – z możliwością ciągłej synchronizacji oraz przy tymczasowej izolacji od centralnego repozytorium.. 3.1 Własności systemu Nadrzędnym zadaniem tworzonego środowiska dla pracy zespołowej jest zarządzanie wprowadzaniem równoległych zmian refaktoryzujących z rozproszonych lokalizacji [80]. Formalnie od środowiska oczekujemy następujących własności: 29.

(30) . otwartość (ang. openness) – projekt może być modyfikowany w reakcji na zewnętrzny proces decyzyjny (najczęściej będzie nim projektantprogramista),. . równoległość (ang. parallelism) – możliwość wprowadzania zmian przez niezależnie pracujących ekspertów,. . rozproszenie (ang. distribution) – zapewnienie możliwości replikacji danych. (do. środowisk. lokalnych. projektantów). potrzebnych. do. podejmowania decyzji, . transparentność czasowa (ang. time transparency) – brak ograniczeń na czas reakcji procesów,. . niepodzielność (ang. atomicity) – żadne z wprowadzonych zmiana nie mogą zostać utracone bez wiedzy i zgody autora,. . globalna spójność (ang. global consistency) – lokalne decyzje nie mogą zaburzyć globalnych reguł,. . trwałość (ang. durability) – decyzje w systemie są nieodwołalne (ich zmiana możliwa jest poprzez stworzenie kolejnej globalnej wersji).. Cechy niepodzielności, rozproszenia i czasowej transparentność pozwolą projektantom (lub specjalnym procesom decyzyjnym) na niezależną prace w trybie rozłączenia od repozytorium globalnego; transparentność zapewni także możliwość występowania dużych odstępów czasowych pomiędzy rozpoczęciem operacji (lub łańcucha operacji) a jej zatwierdzeniem. Tryb taki będzie bardzo użyteczny w praktyce. ze. względu. na. możliwość. izolacji. pracy. oraz. przeprowadzania. eksperymentów. Niepodzielność pozwoli na wprowadzenie łańcuchów operacji, które zawsze będą musiały zostać wykonane w całości bądź też całkowicie odrzucone. Własność globalnej spójności w przypadku refaktoryzacji pozwoli na zachowanie poprawności opisywanego oprogramowania. Wymaganie trwałości przez nieodwracalne decyzje nie stanowi ograniczenia, gdyż nadal możliwe będzie lokalne wycofanie zmian (po synchronizacji skutkować będzie to wprowadzeniem operacji odwrotnej na nowej wersji globalnej).. 30.

(31) Przy zachowaniu powyżej wymienionych własności od systemu oczekuje się, że: . zapewni możliwość wyeliminowania jak największej liczby potencjalnych konfliktów automatycznie (bez pomocy projektanta),. . ostrzeże o pozostałych konfliktach tak szybko jak to możliwe (w momencie ich powstania w środowisku rozproszonym).. System ma również zapewniać możliwie najlepsze wsparcie dla typowych prac projektantów-programistów – a więc pozwalać na takie operacje jak cofanie zmian (włączając w to całe refaktoryzacje i łańcuchy refaktoryzacji), dostęp i podgląd historii zmian czy w dalszej kolejności zarządzanie wersjami tworzonego oprogramowania. Przy tym wszystkim najwyższy priorytet powinien mieć nadal proces sprawnej koordynacji prac członków zespołów.. 3.2 Grafowa reprezentacja oprogramowania Opierając się na doświadczeniach wykorzystania grafów oraz gramatyk grafowych do opisu i wnioskowania w systemach rozproszonych [23, 43, 44, 46], agentowch [47] oraz analizy i przetwarzania obrazów [22, 24] za podstawę reprezentacji oprogramowania w środowisku rozproszonym DRE wybrana została reprezentacja grafowa. Pomysł reprezentacji oprogramowania, a w szczególności modelu, za pomocą grafu jest dość naturalny i szeroko stosowany także w badaniach naukowych [34, 36, 55, 71]. Graf w porównaniu do reprezentacji drzewowej (np. drzewo składni, ang. abstract syntax tree – AST) [29, 34, 64] pozwala ująć nie tylko statyczne związki między elementami programu ale również relacje dynamiczne; można odwzorować takie własności jak dostęp do zmiennych, wywołania metod czy późne wiązania polimorficzne {17]. Najpowszechniej. stosowaną. notacją. w. zakresie. modelowania. oprogramowania jest UML – Unified Modeling Language [6, 28, 61, 73]. Notacja ta pozwala na graficzną reprezentację komponentów oprogramowania oraz związków między nimi i również została oparta na grafie. Dla naszych zastosowań UML okazuje 31.

(32) się być jednak daleko nie wystarczający, ma też pewne braki w kontekście rozważanym w pracy, a w szczególności: . nie umożliwia ujęcia dynamiki i ewolucji oprogramowania,. . nie pozwala na formalne ujęcie, a tym samym na wnioskowanie i dowodzenie,. . nie zapewnia spójnego opisu w sytuacji wykorzystania różnych typów diagramów, pozwalając jedynie na pojedyncze spojrzenia na projekt z rożnych perspektyw.. Z uwagi na powyższe sposób reprezentacji oprogramowania, przyjęty na potrzeby systemu DRE, będzie jedynie na UML wzorowany pod względem mocy opisowej. Dla zapewnienia wymaganych własności systemu, a także w celu sprawnej synchronizacji prac członków zespołu wykorzystamy specjalną strukturę grafu atrybutowanego. Wprowadzony graf modelu odzwierciedlał będzie aktualny stan modelowanego oprogramowania – szczegóły opisane zostały w rozdziale 4. Na poziomie prezentacji koncepcji wprowadzony opis będzie równoważny z diagramami klas UML, zyskamy jednak przede wszystkim możliwość formalnej analizy i wnioskowania. Dzięki reprezentacji refaktoryzacji na wyższym poziomie abstrakcji, za pomocą transformacji grafowych oraz znajomości ich semantyki, można będzie formalne opisać modyfikacje oraz ewolucję oprogramowania. Wykorzystamy w tym celu osiągnięcia z dziedziny teorii grafów [10, 21, 72, 79] i gramatyk grafowych [26, 40].. 3.3 Repozytorium grafowe Do zarządzania i utrzymania grafu w systemie DRE wykorzystamy koncepcję repozytorium. grafowego,. inspirowaną. repozytorium. wprowadzonym. przez. profesora Kotulskiego [49]; termin „repozytorium” stosowany jest celowo aby podkreślić możliwość współbieżnego dostępu do obsługiwanej instancji grafu. Repozytorium grafowe zapewnia wprowadzenie mechanizmów dla: . kontroli generacji grafu,. . rejestracji przeprowadzonych operacji, 32.

(33) . przechowywania historii zmian.. Ponadto, co istotne dla rozwiązania problemu konfliktów refaktoryzacji, repozytorium zapewnia jednoznaczną identyfikację wierzchołkom grafu; będzie ona utrzymywana przez cały czas trwania projektu reprezentowanego oprogramowania. Dostęp do grafu kontrolowany będzie przez specjalny moduł zapewniający właściwą synchronizację odwołań. Oczywistym wyborem jest wsparcie przez repozytorium optymistycznego modelu współpracy w środowisku współbieżnym. Użytkownicy zyskają swobodę decyzji kiedy i jakie refaktoryzacje wykonać; łącznie zmian odbywać się będzie na zasadzie operacji w trybie scalania 3-kierunkowego. Kontrola generacji i ewolucji grafu to podstawowe zadanie repozytorium. W celu jego realizacji wykorzystanie zostanie formalizm gramatyk i transformacji grafowych. Łańcuchy refaktoryzacji w sposób naturalny odpowiadać będą pracom poszczególnych projektantów (lub procesów). Będą traktowane przez system jako operacje niepodzielne i nierozerwalne. Modyfikacje grafu oprogramowania utrzymywanego w repozytorium opisane zostaną za pomocą transformacji grafowych sterowanych specjalnymi diagramami sterującymi DCE – zgodnie ze zmianami wprowadzanymi do modelu (lub kodu) programu w środowiskach lokalnych oraz ich uwarunkowaniem [43, 44]. Za pomocą transformacji. reprezentowane. będą. wszystkie. przekształcenia. modelu. oprogramowania wśród nich refaktoryzacje. Na niskim poziomie refaktoryzacjom lub ich fragmentom odpowiadać będą produkcje gramatyki, którą, w rozdzile 4, zbudujemy do opisu ewolucji oprogramowania.. 3.3.1 Opis za pomocą formalizmu transformacji grafowych Dla reprezentacji idei działania repozytorium grafowego wykorzystamy podejście algebraiczne single pushout (SPO) [10, 19]. Podejście to opracowane zostało przez H. Ehring, M. Pfender i H.J Schneider [20] w celu generalizacji gramatyk ciągowych Chomskiego na grafy. Bazuje ono na koncepcji sklejania grafów. Grafy są traktowane jako specjalny rodzaj algebr (stąd nazwa), gdzie: V jest skończonym, niepustym zbiorem wierzchołków, E jest zbiorem krawędzi, a funkcje s: E  V oraz t: E  V, przyporządkowujące krawędziom wierzchołki startowe (source) i docelowe (target), są operatorami unarnymi (działaniami). 33.

(34) W oparciu o definicją refaktoryzacji 2.1 każde z przekształceń grafu oprogramowania (w tym refaktoryzacje) reprezentuje para: P = (pre, T). Transformacja T: G  H przeprowadza graf G w nową postać H przez zastosowanie specjalnych reguł dopasowanych (ang. match), poprzez odpowiedni homomorfizm, w kontekście grafu G. Modyfikacja grafu (wywód bezpośredni) odbywa się przez zastosowanie produkcji gramatyki (p); produkcja reprezentowana jest przez graf lewej strony (L) oraz graf prawej strony produkcji (R): p: L  R. Określa ona, częściową odpowiedniość między elementami lewej i prawej strony wyznaczając, które wierzchołki i krawędzie pozostaną w grafie, które muszą zostać usunięte, a które utworzone. Zastosowanie produkcji, w ramach transformacji, rozpoczyna się od wyszukania w grafie G pod-grafu homomorficznego do lewej strony produkcji L (homomorfizm h: L  G). Następnie pod-graf ten – h(L) – usuwany jest z grafu G, a w jego miejsce umieszczany jest graf prawej strony produkcji (R). Homomorfizm odwzorowuje wierzchołki i krawędzie L na G w taki sposób, że zachowana zostaje struktura grafowa oraz etykietowanie. Wszystkie elementy z G, które mają odpowiednik w L, a nie występują w R są usuwane (wraz z krawędziami z nimi połączonymi); elementy, które są w R a nie było ich w L są dodawane (wraz z krawędziami występującymi w grafie R). Pozostałe elementy G są zachowane, a więc graf H jest otrzymywany następująco: H = G – (L – R) (R – L) Wywód bezpośredni przy homomorfizmie h oznaczamy jako G p,h H; przedstawia go rysunek 3.1. W ogólności L nie musi być homomorficzne z h(L).. Rysunek 3.1. Wywód bezpośredni G p,h H przy zastosowaniu produkcji p. 34.

(35) Homomorfizm h* jest dopasowaniem między prawa stroną produkcji a jej obrazem w grafie H, z kolei p* odwzorowuje G na H [10].. Gramatykę grafową Gr stanowi zbiór produkcji wraz z grafem startowym G0. Sekwencja wywodów bezpośrednich  = ( G0 p1 G1 p2 ... pn Gn) nazywamy wywodem w gramatyce i oznaczamy G0  Gn. Natomiast językiem L(Gr) generowanym przez gramatykę Gr będzie zbiór grafów Gn takich, że G0  Gn jest wywodem w gramatyce.. Notacja SPO zapewnia bardzo dobrą czytelność opisu i pomimo, że nie jest efektywna dla implementacji budowanego systemu, to zapewnia możliwość formalnego dowodzenia z wykorzystaniem osiągnięć z dziedziny teorii grafów z uwagi na to, iż dowodzenie takie jest w dużym stopniu niezależne od struktury opisywanych obiektów. Efektywność. implementacji. możemy. osiągnąć. zastępując. każdą. z. zaproponowanych w kolejnym rozdziale produkcji typu SPO sekwencją produkcji gramatyki ETPL(k), która pozwala rozwiązać problem generacji nowego grafu jak i wnioskowania o przynależności danego grafu do klasy grafów generowanej przez język (ang. membership problem) ze złożonością obliczeniową O(n 2) [24, 26]. Szczegóły wykorzystania grafu atrybutowanego, mechanizmów sterujących oraz implementacji grafu modelu zostały zamieszczone w rozdziale 4.. 3.3.2 Kontrola wykonania szeregu transformacji Aby mieć możliwość sterowania kolejnością wykonania przekształceń oraz sprawnie je ze sobą łączyć zastosujemy specjalne diagramy sterujące – Derivation Control Diagram (DCD) oraz środowisko wykonania – Derivation Control Environment (DCE). Wykorzystanie DCE oparte jest na powodzeniu wcześniejszego zastosowaniem m.in. do kontroli procesu alokacji w środowisku rozproszonym [43] oraz opisu zachowania systemu agentów mobilnych [47]. W systemie DRE, oprócz kontroli wprowadzanie kompozycji oraz łańcuchów refaktoryzacji, zastosowanie DCE zapewni również wprowadzanie bardziej skomplikowanych refaktoryzacji. 35.

(36) Mechanizm kontroli DCE reprezentowany jest poprzez diagram łączący punkty kontrolne (okręgi kropkowane na rysunku 3.2), w których wykonywane będą sekwencyjnie: funkcja synchronizująca Waitk (jeżeli jest przypisana) oraz selektor Πk. Funkcja Waitk jest wykonywana w oparciu o aktualny stan grafu oraz kolejkę żądań (wysyłanych do DCE). Jeżeli próba wykonania się nie powiedzie to aktywność w danym punkcie kontroli zostanie wstrzymana do czasu zmiany warunków środowiska (np. pojawienia się nowego żądania), w przeciwnym przypadku wykonany zostanie selektor Πk i tym samym wybrane właściwe przejście na diagramie DCD. Wykonanie nie tylko przenosi aktywność do następnego punktu kontrolnego, ale realizowana jest stosowna produkcja gramatyki Pi oraz towarzysząca jej akcja semantyczna SFi. Funkcja semantyczna: . dodaje nowe żądanie do kolejki żądań (wymagając pewnej akcji od systemu refaktoryzacji),. . usuwa obsługiwane żądanie z kolejki,. . rozwija parametry prawej strony produkcji.. Po zastosowaniu transformacji na aktualnym grafie H uzyskujemy nowy graf H' w sposób zdefiniowany przez reguły transformacji gramatyki powiązanej z diagramem sterującym.. Rysunek 3.2. Derivation Control Environment [43]. Wprowadzenie wątków współbieżnego wykonania upraszcza opis DCE, jednak aby zapewnić właściwą modyfikację danych musimy wprowadzić ogólną regułę synchronizacji: każdy wątek kontroli ma wyłączny dostęp do danych reprezentujących. 36.

(37) graf H i do kolejki żądań w czasie od momentu wykonania Wait k do momentu stworzenia grafu H'.. 3.4 Architektura systemu z rozproszonym repozytorium grafowym Środowisko DRE dla tworzenia oprogramowania przez zespoły projektantów wspierające refaktoryzacje (stanowiące system rozproszony) składać się będzie z (rysunek 3.3): . Centralnego. Systemu Zarządzania. Grafem. –. GMS. (ang. Graph. Management System), . dowolnej liczby Lokalnych Środowisk Refaktoryzacji – LRE (ang. Local Refactoring Environment).. Rysunek 3.3. Model pracy Collaborative Software Environment z rozproszonym repozytorium grafowym. Zadaniem GMS jest utrzymanie globalnego repozytorium grafowego oraz zapewnienie transparentnego dostępu (ze środowisk LRE) do obsługiwanego w nim 37.

(38) grafu oprogramowania. Ważna jest separacja odpowiedzialności, tak aby graf odpowiadał tylko i wyłącznie za utrzymania stanu modelowanego oprogramowania. Dostępem współbieżnym do struktury grafu zajmie się specjalny moduł Concurrent Access Coordinator (CAC); nie będziemy jednak zagłębiać się w opis mechanizmów synchronizacji, gdyż nie są one istotne dla tematyki pracy. W węźle centralnym umieszczony został także moduł zarządzający (MI) odpowiedzialny. za. przechowywanie. wszystkich. informacji. niezbędnych. do. utrzymania topologii systemu (włączając w to listę LRE) jak i opisywanego oprogramowania. oraz. rozmieszczenia. komponentów. (wierzchołków. grafu. oprogramowania) replikowanych w środowiskach lokalnych. W każdym ze środowisk LRE utrzymywane będzie repozytorium lokalne z odpowiednim fragmentem grafu oprogramowania; repozytoria grafowe będziemy często utożsamiać ze środowiskiem, w którym występują (globalne z GMS, lokalne z LRE). W węzłach LRE repozytoria grafowe pozwalają na operacje na fragmentach zreplikowanego grafu. W ramach LRE zakładamy jednak pracę w sekcji krytycznej, czyli wykluczamy dostęp równoległy. Procesów decyzyjnych operujących przy pojedynczym LRE może być co prawda kilka (DP1 ... DPk), ale wszystkie będą wzajemnie świadome swojego działania; w przypadku braku takiej możliwości powinny pracować w osobnych LRE. Zauważmy, że takie założenie pozawala na pracę dwóch programistów przy jednym stanowisku zgodnie z założeniami programowania w parach lub wspomaganie projektanta przez proces automatyczny. W praktyce będziemy mieć na ogół do czynienia z pojedynczym procesem przypisany do pojedynczego środowiska lokalnego. Wszystkie repozytoria (w tym lokalne w węzłach LRE) wyposażone zostały w generatory identyfikacji dla nowych komponentów. Ponieważ nie ma możliwości bezpośrednie pracy na GMS, w jego przypadku taki generator będzie wykorzystany w ograniczony sposób. Potrzebna jest natomiast koordynacja podczas przydzielania identyfikacji komponentom, tak aby każde z lokalnych środowisk dysponowało odrębną pulą. Zadanie to przejmuje DCE będące elementem repozytorium – najprostszym rozwiązaniem jest przyznanie przedrostków specyficznych dla konkretnego LRE (bardziej skomplikowanym byłaby renumeracja w węźle GMS). Drugą istotną funkcją DCE jest właściwa aplikacja transformacji grafowych.. 38.

(39) 3.4.1 Przebieg wykonania przekształceń Podczas realizacji przekształcenia (refaktoryzacji) przez proces decyzyjny na DCE, w środowisku lokalnym LRE przeprowadzana będzie następująca operacja: . określenie kontekstu przekształcenia (find_context) – lokalnie lub poprzez zapytanie GMS w celu identyfikacji części grafu powiązanej ze stosowaną operacją,. . przeprowadzanie refaktoryzacji na posiadanej kopii modelu/kodu (DCE wykona transformację grafu),. . przekazanie informacji o przeprowadzonych operacjach do GMS (na żądanie, po zakończeniu etapu pracy),. . zaktualizowanie kodu utrzymywanego w LRE na żądanie GMS.. Pierwsze zadanie wykonywane lokalnie (lub przez GMS) jest prostą akcją semantyczna. wyszukania. informacji w grafie. Drugie związane będzie z. zastosowaniem transformacji modyfikującej graf przechowywany w repozytorium oraz z wykonaniem towarzyszącej akcji semantycznej. W każdym ze środowisk lokalnych prowadzona będzie rejestracja wprowadzanych zmian poprzez nagrywanie operacji. Projektanci wprowadzają zmiany niezależnie (tryb optymistyczny), a następnie współdzielą je po zakończeniu etapu pracy. Mechanizm współdzielenia musi zadbać by nie dopuścić do błędów powodowanych przez współbieżne wykorzystanie tych samych zasobów (pod-grafów) – odpowiedzialność za to przejmuje. moduł. CAC.. Na. zakończenie. następuje. proces. automatycznej. synchronizacji wprowadzonych zmian (całkowicie transparentnie dla procesów zewnętrznych).. 3.4.2 Tryb synchronizacji zmian W zależności od preferencji projektanta (lub w skrajnych przypadkach od możliwości fizycznego połączenia środowiska LRE z repozytorium globalnym GMS) rozróżnić będziemy dwa tryby pracy w środowisku DRE:. 39.

(40) 1) tryb synchronizacji on-line, kiedy środowisko LRE jest w stałym połączeniu z GMS; można wymieniać dane w dowolnym czasie (w skrajnym przypadku również pytać o poprawność każdej z operacji), 2) tryb opóźnionej synchronizacji (off-line) kiedy środowisko lokalne LRE jest odseparowane od globalnego repozytorium (w założeniach na dowolnie długi okres czasu); użytkownik pracuje wyłącznie na zreplikowanych danych, a synchronizacja z globalnym stanem następuje w momencie zdefiniowanym przez użytkownika (np. gdy uzna, że opracował już kompletną sekwencję refaktoryzacji).. W trybie synchronizacji lokalne środowisko pracy projektanta będzie współpracować z repozytorium w sposób całkowicie przeźroczysty. Może również informować o wprowadzanych zmianach w dowolnym momencie, kiedy jest to niezbędne (np. po sekwencji refaktoryzacji). Zwróćmy uwagę, że w celu realizacji własności niepodzielności musimy założyć, iż użytkownik ma możliwość zatwierdzać refaktoryzacje łańcuchami oraz indywidualnie określić kiedy jest gotów współdzielić zmiany z repozytorium globalnym. Taki tryb, mimo iż istnieje stałe połączanie, podobny będzie do pracy offline. Zaletą rozwiązania jest możliwość eksperymentowania (wprowadzania zmian i wycofywania ich) przez projektanta bez informowania i wpływu na prace reszty zespołu. Użytkownik ma możliwość samodzielnego przejścia w odpowiedni tryb na czas zmian i eksperymentów – zrobi to np. pracując nad większym fragmentem systemu lub badając użyteczność wprowadzenia zbioru refaktoryzacji. Środowisko. lokalne. może. na. bieżąco. otrzymywać. informacje. o. refaktoryzacjach prowadzonych przez innych członków zespołu (kiedy zatwierdzą je do repozytorium globalnego) pod warunkiem pracy na tym samym fragmencie grafu; projektant będzie informowany o aktywności innych projektantów jedynie w sytuacji gdy zachodzi groźba niezgodności zatwierdzonych refaktoryzacji ze zmianami lokalnymi. Należy zauważyć, iż nawet przy pracy w trybie stałej synchronizacji wprowadzanie łańcuchów refaktoryzacji komplikacje sytuację – od rozpoczęcia projektowania łańcucha do jego zatwierdzenia może bowiem upłynąć wiele godzin. Wysokopoziomowe rozwiązanie sprawnego łączenia łańcuchów refaktoryzacji 40.

(41) wprowadzanych asynchronicznie w sposób rozproszonych opisane zostanie w rozdziale 6.. 3.5 Podsumowanie Przedstawiona koncepcja architektury systemu DRE opartej na repozytorium grafowym z dostępem współbieżnym zakłada realizacją rozproszonego i częściowo zreplikowanego. repozytorium. struktury. oprogramowania. podlegającego. refaktoryzacji, przy założeniu, że refaktoryzacja tego oprogramowania odbywa się lokalnie w środowiskach LRE. System zakłada, że mogą występować duże odstępy czasowe pomiędzy momentem rozpoczęcia lokalnej operacji, a momentem decyzji o zatwierdzeniu wprowadzonych zmian z repozytorium (zatwierdzenia łańcuch refaktoryzacji). Celem systemu jest więc w pierwszej kolejności – utrzymanie spójności oprogramowania, nawet gdy równolegle podjęte decyzje refaktoryzacyjne prowadzą do konfliktu. W drugiej kolejności, system musi starać się zminimalizować koszt (po stronie zespołów projektowych) rozwiązywania konfliktów, gdy mechanizm synchronizacji nie będzie w stanie wykluczyć ich całkowicie (zostanie to szczegółowo przeanalizowane w rozdziale 5).. 41.

(42) Rozdział 4 Implementacja repozytorium grafowego Zgodnie z założeniem reprezentacji oprogramowania w systemie DRE wykorzystywana struktura grafu atrybutowanego z dodatkowym indeksowaniem pozwalać będzie na efektywną identyfikację komponentów modelu oprogramowania na poziomie równoważnym diagramom klas UML [27, 61]. Zbudujemy gramatykę grafową będącą właściwą platforma do budowy i reprezentacji modelu oprogramowania. Wspomagać będzie ona formalną specyfikację modelu w środowisku rozproszonym [80] ze szczególnym uwzględnieniem opisu ewolucji oprogramowania zapewniając przy tym możliwość kontroli przeprowadzania refaktoryzacji. Wszelkie modyfikacje oprogramowania reprezentowane będą za pomocą szeregu spośród zbioru specjalnie dobranych operacji elementarnych; na poziomie repozytorium odpowiadać im będą transformacje grafowe. W rozdziale transformacje zaprezentowane zostaną zgodnie z podejściem algebraicznym SPO [10, 20]. Wykorzystując diagramy sterujące DCD [43] pokazany zostanie mechanizm kontroli kompozycji transformacji.. 4.1 Struktura grafu oprogramowania Model dowolnego programu reprezentować będziemy przez graf skierowany, atrybutywny i etykietowany zarówno wierzchołkowo jak i krawędziowo. Wierzchołki grafu odpowiadać będą poszczególnym komponentom oprogramowania, a więc definicjom klas (typów), zmiennych, metod oraz parametrów metod. W pracy, dla uproszczenia opisu i w celu prezentacji jedynie istotnych własności repozytorium,. 42.

Cytaty

Powiązane dokumenty

W poniższej tabeli przedstawiono rozkład procentowy ich odpowiedzi (gwiazdką oznaczono od- powiedź poprawną). Naj- częściej wybieranym dystraktorem była odpowiedź A –

Uczestnicy przedsięwzięcia – dzieci, młodzież i ich ro- dzice i opiekunowie – będą mogli wziąć udział w krót- kich wykładach, warsztatach praktycznych, zajęciach

Ufam, że wyniki naszych badań choć w niewielkim stopniu przyczynią się do poznania wspaniałego daru języka, który dany jest człowiekowi i wspólnocie dla realizacji

Dysfunctions of the mitochondrial proteins lead to the mitochondrial diseases, which can be caused by muta- tions in mtDNA as well as in the nuclear genes.. Clinical features of

Obawy przed marginalizacją języka, jak i próby wyjaśniania, że będzie on jednym z języków urzędowych w Unii, to najczęściej pojawiające się tematy, które można odnaleźć

dętka rowerowa,  sprzączka lub klamra ze  starego paska, nożyczki,  zszywacz automatyczny lub igła i nitka. Krok

Creëer daarom visuele toegang tot groen binnen en buiten, zorg dat patiënten zelf gemakkelijk naar buiten kunnen, zorg voor afbeeldingen van natuurlijke omgevingen in