|
|
(Nie pokazano 2 pośrednich wersji utworzonych przez tego samego użytkownika) |
Linia 1: |
Linia 1: |
| =Ćwiczenie 13. Refaktoryzacja oprogramowania w Javie za pomocą Eclipse IDE= | | =Ćwiczenie 6. Wzorce projektowe cz. II= |
|
| |
|
| ==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
| | Ćwiczenie jest kontynuacją cyklu związanego z wzorcami projektowymi. Celem jest zastosowanie kolejnych wzorców 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.
| | Ćwiczenie jest kontynuacją poprzedniego, dlatego należy prowadzić prace w dotychczasowym projekcie. |
|
| |
|
| 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 w systemie bankowym mechanizm naliczania oprocentowania w postaci wzorca State. Uwzględnij trzy rodzaje oprocentowania: |
| | * oprocentowanie typu A: 2% do kwoty 800 PLN oraz 4% od kwoty powyżej 800 PLN |
| | * oprocentowanie typu B: 3% do kwoty 1000 PLN oraz 5% od kwoty powyżej 1000 PLN |
| | * oprocentowanie typu C: 3.5%, niezależnie od wartości salda na rachunku klienta |
| | Mechanizmy naliczania oprocentowania należy zaimplementować w postaci osobnych klas, dziedziczących po wspólnej nadklasie lub implementujących wspólny interfejs. |
|
| |
|
| ==Zadanie==
| | Zmiana oprocentowania związanego z rachunkiem bankowym polega na zmianie bieżącego obiektu reprezentującego oprocentowanie. Polecenia naliczenia odsetek powinno być delegowane do bieżącego obiektu reprezentującego oprocentowanie. |
| Na podstawie załączonego kodu programu przeprowadź następujące zmiany w programie, korzystając z mechanizmów refaktoryzacyjnych obecnych w Eclipse IDE.
| | Operacja zmiany odsetek powinna być zaimplementowana podobnie jak inne operacje bankowe, tj. jako realizacja wzorca Command. |
|
| |
|
| ===Wyłączenie metody===
| | Zwróć uwagę, aby obiekty reprezentujące oprocentowanie były bezstanowe, tzn. nie przechowywały informacji związanej z rachunkiem (np. wysokości salda). Referencja do rachunku powinna być przekazywana obiektowi reprezentującemu oprogramwanie jako parametr metody obliczającej odsetki. |
|
| |
|
| 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.
| | ==Zadanie 2== |
| | Wyłącz z klasy Rachunek interfejs ProduktBankowy, który może posiadać także inne implementacje (np. Lokata, Kredyt). Następnie stwórz metodę-fabrykę, która pozwoli hermetyzować sposób tworzenia instancji produktu bankowego. Powinna ona przyjmować parametry, które określają typ tworzonego obiektu. |
|
| |
|
| # 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'')
| | # Wzorzec Prototype jest zaimplementowany w języku Java w postaci metody ''clone()'', wykonującej tzw. płytką kopię obiektu. Dlaczego domyślnie nie jest wykonywana głęboka kopia obiektu? |
| # 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.
| | # Co spowodowałoby uczynienie ogniw łańcucha we wzorcu Chain of Responsibility obiektami przechowującymi stan żądania? |
| # Możesz kliknąć klawisz ''Preview'' - zostanie wówczas wyświetlona nowotworzona metoda.
| |
| # 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. II
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.
Ćwiczenie jest kontynuacją cyklu związanego z wzorcami projektowymi. Celem jest zastosowanie kolejnych wzorców 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
Ćwiczenie jest kontynuacją poprzedniego, dlatego należy prowadzić prace w dotychczasowym projekcie.
Zadanie 1
Zaimplementuj w systemie bankowym mechanizm naliczania oprocentowania w postaci wzorca State. Uwzględnij trzy rodzaje oprocentowania:
- oprocentowanie typu A: 2% do kwoty 800 PLN oraz 4% od kwoty powyżej 800 PLN
- oprocentowanie typu B: 3% do kwoty 1000 PLN oraz 5% od kwoty powyżej 1000 PLN
- oprocentowanie typu C: 3.5%, niezależnie od wartości salda na rachunku klienta
Mechanizmy naliczania oprocentowania należy zaimplementować w postaci osobnych klas, dziedziczących po wspólnej nadklasie lub implementujących wspólny interfejs.
Zmiana oprocentowania związanego z rachunkiem bankowym polega na zmianie bieżącego obiektu reprezentującego oprocentowanie. Polecenia naliczenia odsetek powinno być delegowane do bieżącego obiektu reprezentującego oprocentowanie.
Operacja zmiany odsetek powinna być zaimplementowana podobnie jak inne operacje bankowe, tj. jako realizacja wzorca Command.
Zwróć uwagę, aby obiekty reprezentujące oprocentowanie były bezstanowe, tzn. nie przechowywały informacji związanej z rachunkiem (np. wysokości salda). Referencja do rachunku powinna być przekazywana obiektowi reprezentującemu oprogramwanie jako parametr metody obliczającej odsetki.
Zadanie 2
Wyłącz z klasy Rachunek interfejs ProduktBankowy, który może posiadać także inne implementacje (np. Lokata, Kredyt). Następnie stwórz metodę-fabrykę, która pozwoli hermetyzować sposób tworzenia instancji produktu bankowego. Powinna ona przyjmować parametry, które określają typ tworzonego obiektu.
Pytania do dyskusji
- Wzorzec Prototype jest zaimplementowany w języku Java w postaci metody clone(), wykonującej tzw. płytką kopię obiektu. Dlaczego domyślnie nie jest wykonywana głęboka kopia obiektu?
- Co spowodowałoby uczynienie ogniw łańcucha we wzorcu Chain of Responsibility obiektami przechowującymi stan żądania?
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.