Celem laboratorium jest zapoznanie się z interfejsem Map oraz wzorcem projektowym Observer.
- Interfejs
Mapdefiniuje w Javie strukturę słownikową, czyli mapę odwzorowującą klucze na wartości. - Jedną z najczęściej wykorzystywanych implementacji interfejsu
Mapjest klasaHashMap, przykładowo:
Map<Vector2d, Animal> animals = new HashMap<>();-
Poprawne działanie
HashMapuzależnione jest od implementacji metodequalsorazhashCodew klasie, która stanowi klucze mapy (w ćwiczeniu dotyczy to klasyVector2d). -
Wynik działania metody
hashCodemusi być zgodny z wynikiem działania metodyequals, tzn. jeśli dwa obiekty są równe wedługequals, to ichhashCodemusi być równy. -
Przykładowa implementacja metody
hashCodedla klasyVector2dmoże wyglądać następująco:
@Override
public int hashCode() {
return Objects.hash(this.x, this.y);
}-
Używanie mapy nie wymaga jawnego wywoływania metody
hashCode, ale jest ona używana wewnętrznie dla potrzeb optymalizacji. Istotą kodu nie są konkretne wartości, przez które mnożone są składnikixiyale fakt, że dla identycznych wartościxiywartość funkcjihashCodebędzie identyczna. -
Wzorce projektowe są koncepcją występującą w programowaniu obiektowym polegającą na tym, że określona klasa problemów może być rozwiązana w schematyczny sposób. Rozwiązanie problemu jednak nie może być (najczęściej) zawarte w jednej klasie, dlatego wzorzec stanowi swego rodzaju szkielet rozwiązania, który określa jakie klasy i interfejsy muszą być wykorzystane, aby poprawnie rozwiązać dany problem.
-
Przykładem wzorca jest obserwator (observer) - rozwiązuje on problem zmian wewnętrznego stanu obiektu. Więcej informacji na temat tego wzorca można znaleźć pod adresem https://en.wikipedia.org/wiki/Observer_pattern
-
W Javie istnieje kolekcja
SortedSet, która umożliwia przechowywanie uporządkowanego zbioru elementów. Elementy mogą implementować interfejsComparablelub przy tworzeniu zbioru należy wskazać obiekt implementujący interfejsComparator, odpowiedzialny za porządkowanie elementów. PrzekazanyComparatorzastępuje naturalny porządek sortowania (wynikający z interfejsuComparable), jeśli taki jest.
- Implementacja metod
isOccupiedorazobjectAtw mapach nie jest wydajna, ponieważ za każdym razem wymaga przejścia przez wszystkie elementy znajdujące się na mapie. Można ją poprawić zamieniając listę na słownik (wykorzystując interfejsMaporaz implementacjęLinkedHashMap) albo dodając obok listy zwierząt osobne pole będące mapą (w tym wypadku wystarczy klasaHashMap). Jest to jednocześnie wydajniejsze pamięciowo niż przechowywanie zwierząt (i trawy) w tablicy. Kluczami słownika powinny być pozycje elementów, a wartościami konkretne obiekty. - Poprawna implementacja słownika wymaga, aby klasa
Vector2dimplementowała metodęhashCode. Metoda ta jest wykorzystywana m.in. przez słownik oparty o tablicę haszującą (HashMap). Możesz wygenerować kod metodyhashCodew klasieVector2dkorzystając ze wsparcia środowiska programistycznego. Zasadniczo metoda ta musi być zgodna z działaniem metodyequals- dwa obiekty, które są równe według metodyequalsmuszą mieć identyczną wartość zwracaną przez metodęhashCode(nie działa to w drugą stronę -hashCodemoże zwracać równe wartości dla obiektów, które nie są równe wg.equals). - Zmiana typu kolekcji
animalsbędzie wymagała zmiany implementacji metodisOccupiediobjectAt.
- Implementacja mechanizmu aktualizacji słownika mapy wymaga, aby mapa była informowana o zmianach pozycji zwierząt, które inicjuje
SimulationEngine. Rozwiązaniem jest zastosowanie wzorca projektowegoObserver- mapa ma zarejestrować się jako obserwator dla zmian pozycji zwierzęcia. - Realizację implementacji rozpocznij od zdefiniowana interfejsu
IPositionChangeObserver, który zawiera jedną metodępositionChanged(Vector2d oldPosition, Vector2d newPosition). - Obie mapy muszą implementować ten interfejs. Możesz to również zrealizować, jeśli odpowiedni kod umieścisz w klasie
AbstractWorldMap. Implementacja metodypositionChangedpowinna polegać na tym, że ze słownika usuwana jest para:<stara pozycja, zwierzę>, a dodawana jest para:<nowa pozycja, zwierzę>. - Klasa
Animalmusi umożliwić rejestrowanie obserwatorów. Dodaj metody:void addObserver(IPositionChangeObserver observer)orazvoid removeObserver(IPositionChangeObserver observer), które będą dodawały i usuwały danego obserwatora do listy obserwatorów w klasieAnimal. - Klasa
Animalmusi informować wszystkich obserwatorów, o tym że pozycja została zmieniona. Stwórz metodępositionChangedw klasieAnimal, która będzie notyfikowała wszystkich obserwatorów o zmianie. - Zweryfikuj poprawność implementacji korzystając z kodu z poprzednich laboratoriów.
- Otaguj gotowe rozwiązanie jako lab6.