|
|
Linia 1: |
Linia 1: |
| =Ćwiczenie 9. Refaktoryzacja oprogramowania w Javie za pomocą Eclipse IDE= | | =Ćwiczenie 6. Wzorce projektowe cz. I= |
|
| |
|
| ==Informacje wstępne== | | ==Informacje wstępne== |
| Ćwiczenie jest realizowane w języku Java w środowisku Eclipse 3.2. | | Ćwiczenie jest realizowane w języku Java w środowisku Eclipse 3.2. |
| Przed przystąpieniem do realizacji ćwiczenia należy zapozanać się z [http://www-128.ibm.com/developerworks/library/os-ecref|Artykułem nt. refaktoryzacji w Eclipse IDE] | | Przed przystąpieniem do realizacji ćwiczenia należy zapozanać się z wykładem. |
| Celem ćwiczenia jest wykonanie przekształceń refaktoryzacyjnych wybranych fragmentów kodu znajdujących się w załączonym projekcie | | Celem ćwiczenia jest zastosowanie wybranych wzorców projektowych w przykładowym systemie obiektowym dotyczącym [[ZPO-bank|banku]]. |
|
| |
|
| ==Instalacja== | | ==Instalacja== |
| Przygotowanie do ćwiczenia polega na zainstalowaniu JDK 5.0 [http://java.sun.com/javase/downloads/index.jsp] oraz środowiska Eclipse [http://www.eclipse.org/downloads] w wersji 3.2 | | Przygotowanie do ćwiczenia polega na zainstalowaniu JDK 5.0 [http://java.sun.com/javase/downloads/index.jsp] oraz środowiska Eclipse [http://www.eclipse.org/downloads] w wersji 3.2 |
| Po uruchomieniu środowiska należy zaimportować (menu File->Import->Existing projects into workspace) [[media:Sudoku.zip|projekt Sudoku]] autorstwa Henninga Vittinga, dostępny w serwisie SourceForge.net. | | Po uruchomieniu środowiska należy zaimportować (menu File->Import->Existing projects into workspace) [[media:zpo-5-lab.zip|projekt]], w którym będą stosowane wzorce projektowe. |
|
| |
|
| Ponadto należy zaimportować projekt [[media:zpo-9-lab.zip|zpo-9-lab.zip]], niezbędny do prezentacji przekształcenia rozwinięcia zmiennej.
| | ==Zadanie 1== |
| | Zaimplementuj operacje bankowe w taki sposób, aby stanowiły realizację wzorca Command. Każda operacja powinna być niezależną klasą, implementacją wspólnego interfejsu ''OperacjaBankowa'' i posiadać metodę ''wykonaj()'', która powoduje wykonanie specyficznej operacji. Przekazanie parametrów specyficznych dla poszczególnych operacji odbywa się poprzez konstruktor operacji. |
|
| |
|
| ==Zadanie==
| | Należy zaimplementować operacje wpłaty, wypłaty i przelewu. |
| Na podstawie załączonego kodu programu przeprowadź następujące zmiany w programie, korzystając z mechanizmów refaktoryzacyjnych obecnych w Eclipse IDE.
| |
|
| |
|
| ===Wyłączenie metody=== | | ==Zadanie 2== |
| | Zaimplementuj w systemie bankowym mechanizm pozwalający bankowi przekazywać tym właścicielom rachunków, którzy wyrażą na to zgodę, komunikaty. W tym celu zastosuj wzorzec Observer, w którym obiektem obserwowanym jest bank, a obserwatorami - właściciele. |
|
| |
|
| Wyłączanie metody (ang. ''Extract Method'') jest najprostszym przekształceniem refaktoryzacyjnym. Jest stosowane zwykle w stosunku do metod o zbyt dużej złożoności lub zbyt długich.
| | Właściciel rachunku jest obiektem, a docierające do niego komunikaty są dołączane do listy przechowywanej przez Właściciela. |
|
| |
|
| # W projekcie SuDoKu znajdź metodę o dużej złożoności (o wysokiej wartości złożoności McCabe'a), np. ''com.vitting.rcpsudoku.model.SudokuDocument.load()''.
| | ==Pytania do dyskusji== |
| # Zaznacz fragment metody, w którym nie są wykorzystywane zmienne lokalne (np. linie 65-71 w klasie ''SudokuDocument'')
| | # Czy zastosowanie odpowiednio spreparowanego obiektu obserwatora we wzorcu Observer może zagrozić stabilności aplikacji? Jak musiałby wyglądać ten obiekt? |
| # Następnie z menu kontekstowego wybierz opcję Refactor->ExtractMethod. Pojawi się okienko, w którym należy wprowadzić nazwę metody (np. canReadVerification w przypadku klasy SudokuDocument) oraz jej zakres widoczności. Zwróć uwagę, że parametry metody są obliczane automatycznie na podstawie zmiennych zdefiniowanych poza zaznaczonym fragmentem, a do których istnieją odwołania wewnątrz niego.
| | # Czy zastosowanie wzorca Pool of Objects w stosunku do obiektów bezstanowych ma sens? Jeżeli tak, to w jakich sytuacjach? |
| # Możesz kliknąć klawisz ''Preview'' - zostanie wówczas wyświetlona nowotworzona metoda.
| | # W jaki sposób można zastąpić obiekty Singleton rozwiązaniami o równoważnej funkcjonalności? |
| # Kliknij klawisz OK - wówczas zostanie utworzona nowa metoda o podanej nazwie, a w miejscu zaznaczonego fragmentu kodu zostanie umieszczone wywołanie tej metody.
| |
| | |
| [[image:extract-method.png]]
| |
| | |
| Jednak wyłączenie metody nie zawsze jest wykonalne. Spróbuj wyłączyć fragment tej samej metody pomiędzy liniami 138 i 145. Tym razem Eclipse zgłosi brak możliwości wykonania przekształcenia. Wytłumacz, dlaczego?
| |
| | |
| ===Zmiana sygnatury metody=== | |
| | |
| Przekształcenie to jest złożeniem dwóch refaktoryzacji: dodania parametru i usunięcia parametru, a także pozwala zmieniać typ metody i zasięg jej widoczności. Służy właśnie do modyfikowania tych elementów metody, w tym przede wszystkim listy parametrów przekazywanych metodzie.
| |
| | |
| # Wskaż kursorem sygnaturę metody ''save()'' w klasie ''com.vitting.rcpsudoku.jfc.actions.SaveAction()'' | |
| # Z menu kontekstowego wybierz opcję Refactor->Change Method Signature
| |
| # Pojawi się okienko, w którym możesz zmodyfikować wspomniane wcześniej wartości
| |
| # Dodaj do sygnatury nowy parametr typu ''Object'' o nazwie ''obj''. Domyślną wartością może być dowolne wyrażenie, którego wartość jest określonego typu (w tym przypadku ''Object''), np. ''new Object()''.
| |
| # Kliknij klawisz Preview. Pojawi się okienko z listą plików, które zostaną zmodyfikowane, oraz zakres zmian w każdym pliku.
| |
| # Kliknij OK. Sygnatura metody zostanie zmodyfikowana, a wszystkie wywołania tej metody zostaną poszerzone o nowy argument o wartości ''new Object()''.
| |
| | |
| Jednak istnieją sytuacje, w których to przekształcenie nie może zostać poprawnie zakończone.
| |
| # Wskaż kursorem metodę, która jest wymagana przez interfejs (np. metodę ''controlEnabled()'' w klasie ''com.vitting.rcpsudoku.jfc.actions.SaveAction()'').
| |
| # Z menu kontekstowego wybierz opcję Refactor->Change Method Signature.
| |
| # Pojawi się ostrzeżenie. Co jest jego przyczyną?
| |
| [[image:change-signature.png]]
| |
| | |
| ===Przeniesienie składowej===
| |
| | |
| Przekształcenie to służy do przeniesienia składowej (pola lub metody) do innej klasy. Stosowane jest zwykle w sytuacji, gdy odwołania do tej składowej w większości pochodzą z innej klasy.
| |
| | |
| W przypadku składowej statycznej może ona zostać przeniesiona do dowolnej klasy.
| |
| | |
| # Wskaż kursorem dowolną składową statyczną (np. pole ''PROPERTY_FILE_NAME'' w klasie ''com.vitting.sudoku.jfc.GuiPersistence''. | |
| # Z menu kontekstowego wybierz opcję Refactor->Move
| |
| # Pojawi się okienko z prośbą o wskazanie klasy, do której składowa ma zostać przeniesiona
| |
| # Kliknij Preview. Pojawi się okienko podglądu z listą modyfikowanych klas oraz zakresem zmian.
| |
| # Kliknij OK. Składowa zostanie przeniesiona
| |
| [[image:move-static.png]]
| |
| | |
| Natomiast w przypadku składowych niestatycznych (tzn. należących do instancji klasy) składowa może zostać przeniesiona do obiektu, który jest polem klasy źródłowej (jest to jedyna możliwość w przypadku przenoszenia pól) lub argumentem przenoszonej metody (w przypadku przenoszenia metody).
| |
| | |
| Przeniesienie metody powoduje również przekazanie w metodzie referencji do obiektu źródłowego.
| |
| | |
| # Wskaż kursorem metodę ''isEditMode()'' w klasie ''com.vitting.sudoku.jfc.MainWindow''
| |
| # Z menu kontekstowego wybierz opcję Refactor->Move
| |
| # Pojawi się okienko, w którym zostaną wyświetlone potencjalne klasy docelowe (w tym przypadku będą to wyłącznie pola klasy źródłowej, ponieważ przenoszona metoda nie posiada argumentów). Okienko to pozwala na zmianę nazwy metody podczas przenoszenia oraz określenie nazwy parametru z obiektem źródłowym
| |
| # Kliknij Preview. Zostanie wyświetlone ostrzeżenie o zmiane zasięgu widoczności pola ''editMode''. Kliknij Continue
| |
| # Zostanie wyświetlona lista modyfikowanych klas wraz z zakresem zmian. Przeanalizuj je.
| |
| # Kliknij OK. Metoda zostanie przeniesiona
| |
| [[image:move-nonstatic-target.png]]
| |
| | |
| ===Rozwinięcie===
| |
| | |
| Przekształcenie to służy do rozwinięcia zmiennej (ang. Inline Temp) lub metody (ang. Inline Method), czyli jest komplementarne np. w stosunku do przekształcenia Extract Method
| |
| | |
| # Wskaż kursorem wywołanie metody ''setMessage(message, NORMAL)'' w metodzie ''setMessage(String)'' w klasie ''com.vitting.sudoku.jfc.MainWindow''
| |
| # Z menu kontekstowego wybierz opcję Refactor->Inline
| |
| # Pojawi się okienko, w którym należy dokonać wyboru pomiędzy zastąpieniem wszystkich wywołań tej metody jej ciałem, czy tylko wskazanego wywołania. W przypadku wyboru wszystkich wywołań metoda ''setMessage(String, int)'' stanie się nie wykorzystywana i będzie mogła być usunięta. Wybierz opcję ''All Invocations''
| |
| # Pojawi się okienko podglądu z listą modyfikowanych klas i zmianami, jakie zostaną wprowadzone. Przeanalizuj je. Zauważ, że rozwijana metoda została usunięta (jeżeli w poprzednim kroku zaznaczono opcję usunięcia metody).
| |
| [[image:inline-scope.png]]
| |
| | |
| Podobnie jak w poprzednich przypadkach, przekształcenie to nie zawsze może być poprawnie wykonane. W celu zaobserwowania takiego zachowania, zaimportuj projekt [[media:zpo-9-lab.zip]] i spróbuj wykonać rozwinięcie zmiennej ''st''. Eclipse wykona przekształcenie, jednak wynik działania programu będzie inny. Dlaczego?
| |
| | |
| ==Przeniesienie składowych w obrębie hierarchii dziedziczenia==
| |
| | |
| Do tej kategorii należą przekształcenia ''Pull up'' i ''Push down''. Służą one odpowiednio do przeniesienia składowej do nadklasy lub do podklasy. W obu przypadkach podstawowym warunkiem wykonania przekształcenia jest istnienie w projekcie odpowiednio nadklasy (lub implementowanego interfejsu) i podklasy w postaci źródłowej.
| |
| | |
| # Wskaż kursorem metodę ''save()'' w klasie ''com.vitting.sudoku.jfc.actions.SaveAction''
| |
| # Z menu kontekstowego wybierz opcję Refactor->Pull Up
| |
| # Pojawi się okienko, w którym należy wskazać docelowy typ, do którego ma być przeniesiona metoda. W tym przypadku jedynym dopuszczalnym typem jest interfejs ''IAction'', ponieważ pozostałe (klasa ''JButton'' oraz interfejs ActionListener'' są dostępne jedynie jako binaria. Za pomocą klawisza Set Required można dołączyć inne składowe, które są wykorzystywane przez przenoszoną metodę, i które także mogą być przeniesione wraz z nią. Klawisz Set Action służy do określenia typu wykonywanej akcji. | |
| # Klikając kolejno klawisze Next wykonaj przekształcenie, analizując poszczególne etapy.
| |
| | |
| ===Hermetyzacja pola===
| |
| | |
| Za pomocą środowiska Eclipse wykonaj przekształcenie hermetyzacji pola (ang. Encapsulate Field) na zmiennej ''saveFile'' w klasie ''com.vitting.sudoku.model.SudokuDocument''
| |
| | |
| ===Wyłączenie nadklasy===
| |
| Za pomocą środowiska Eclipse wykonaj przekształcenie utworzenia nadklasy klasy ''com.vitting.sudoku.model.SudokuDocument''. Przenieś do niej wszystkie metody z klasy ''com.vitting.sudoku.model.SudokuDocument''.
| |
|
| |
|
| ==Literatura== | | ==Literatura== |
| # D. Gallardo "Refactoring for everyone" [http://www-128.ibm.com/developerworks/library/os-ecref IBM DeveloperWorks] | | # E. Gamma, R. Helm, R. Johnson, J. Vlissides "Design Patterns: Elements of Reusable Object-Oriented Software". Addison-Wesley, 1995. |
| | # D. Gallardo "Java Design Patterns 101". [[http://www-128.ibm.com/developerworks/edu/j-dw-javapatt-i.html IBM]] |
| | # J. Cooper "Java. Wzorce projektowe". Helion, 2001 |
| | # A. Shalloway, J. R. Trott "Programowanie zorientowane obiektowo. Wzorce projektowe". Wydanie II, Helion, 2005. |
Ćwiczenie 6. Wzorce projektowe cz. I
Informacje wstępne
Ćwiczenie jest realizowane w języku Java w środowisku Eclipse 3.2.
Przed przystąpieniem do realizacji ćwiczenia należy zapozanać się z wykładem.
Celem ćwiczenia jest zastosowanie wybranych wzorców projektowych w przykładowym systemie obiektowym dotyczącym banku.
Instalacja
Przygotowanie do ćwiczenia polega na zainstalowaniu JDK 5.0 [1] oraz środowiska Eclipse [2] w wersji 3.2
Po uruchomieniu środowiska należy zaimportować (menu File->Import->Existing projects into workspace) projekt, w którym będą stosowane wzorce projektowe.
Zadanie 1
Zaimplementuj operacje bankowe w taki sposób, aby stanowiły realizację wzorca Command. Każda operacja powinna być niezależną klasą, implementacją wspólnego interfejsu OperacjaBankowa i posiadać metodę wykonaj(), która powoduje wykonanie specyficznej operacji. Przekazanie parametrów specyficznych dla poszczególnych operacji odbywa się poprzez konstruktor operacji.
Należy zaimplementować operacje wpłaty, wypłaty i przelewu.
Zadanie 2
Zaimplementuj w systemie bankowym mechanizm pozwalający bankowi przekazywać tym właścicielom rachunków, którzy wyrażą na to zgodę, komunikaty. W tym celu zastosuj wzorzec Observer, w którym obiektem obserwowanym jest bank, a obserwatorami - właściciele.
Właściciel rachunku jest obiektem, a docierające do niego komunikaty są dołączane do listy przechowywanej przez Właściciela.
Pytania do dyskusji
- Czy zastosowanie odpowiednio spreparowanego obiektu obserwatora we wzorcu Observer może zagrozić stabilności aplikacji? Jak musiałby wyglądać ten obiekt?
- Czy zastosowanie wzorca Pool of Objects w stosunku do obiektów bezstanowych ma sens? Jeżeli tak, to w jakich sytuacjach?
- W jaki sposób można zastąpić obiekty Singleton rozwiązaniami o równoważnej funkcjonalności?
Literatura
- E. Gamma, R. Helm, R. Johnson, J. Vlissides "Design Patterns: Elements of Reusable Object-Oriented Software". Addison-Wesley, 1995.
- D. Gallardo "Java Design Patterns 101". [IBM]
- J. Cooper "Java. Wzorce projektowe". Helion, 2001
- A. Shalloway, J. R. Trott "Programowanie zorientowane obiektowo. Wzorce projektowe". Wydanie II, Helion, 2005.