Celem laboratorium jest zapoznanie się z interfejsem Map
oraz wzorcem projektowym Observer
.
- Interfejs
Map
definiuje w Javie strukturę słownikową, czyli mapę odwzorowującą klucze na wartości. - Jedną z najczęściej wykorzystywanych implementacji interfejsu
Map
jest klasaHashMap
, przykładowo:
Map<Vector2d, Animal> animals = new HashMap<>();
-
Poprawne działanie
HashMap
uzależnione jest od implementacji metodequals
orazhashCode
w klasie, która stanowi klucze mapy (w ćwiczeniu dotyczy to klasyVector2d
). -
Wynik działania metody
hashCode
musi być zgodny z wynikiem działania metodyequals
, tzn. jeśli dwa obiekty są równe wedługequals
, to ichhashCode
musi być równy. -
Przykładowa implementacja metody
hashCode
dla klasyVector2d
moż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ładnikix
iy
ale fakt, że dla identycznych wartościx
iy
wartość funkcjihashCode
bę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ć interfejsComparable
lub przy tworzeniu zbioru należy wskazać obiekt implementujący interfejsComparator
, odpowiedzialny za porządkowanie elementów. PrzekazanyComparator
zastępuje naturalny porządek sortowania (wynikający z interfejsuComparable
), jeśli taki jest.
- Implementacja metod
isOccupied
orazobjectAt
w 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 interfejsMap
oraz 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
Vector2d
implementowała metodęhashCode
. Metoda ta jest wykorzystywana m.in. przez słownik oparty o tablicę haszującą (HashMap
). Możesz wygenerować kod metodyhashCode
w klasieVector2d
korzystając ze wsparcia środowiska programistycznego. Zasadniczo metoda ta musi być zgodna z działaniem metodyequals
- dwa obiekty, które są równe według metodyequals
muszą mieć identyczną wartość zwracaną przez metodęhashCode
(nie działa to w drugą stronę -hashCode
może zwracać równe wartości dla obiektów, które nie są równe wg.equals
). - Zmiana typu kolekcji
animals
będzie wymagała zmiany implementacji metodisOccupied
iobjectAt
.
- 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 metodypositionChanged
powinna polegać na tym, że ze słownika usuwana jest para:<stara pozycja, zwierzę>
, a dodawana jest para:<nowa pozycja, zwierzę>
. - Klasa
Animal
musi 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
Animal
musi informować wszystkich obserwatorów, o tym że pozycja została zmieniona. Stwórz metodępositionChanged
w 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.