Rozbudowa aplikacji
Cześć!
Czwartą część Wyzwania zaczniemy od omówienia zadania domowego. Jak pamiętasz, zadanie zawierało polecenie, by napisać metodę, która zweryfikuje, czy lot o podanym lotnisku startowym i końcowym istnieje w bazie (czyli w naszej kolekcji lotów).
Oto przykładowe rozwiązanie:


Przeanalizujmy ten kod. Główna logika skupia się w metodzie checkIfFlightExists
. Najpierw tworzymy zmienną boolean notExists
ze startową wartością true
(zakładamy, że dany lot nie istnieje). Za pomocą pętli przechodzimy po wszystkich elementach z listy lotów. Pobieramy każdy lot, sprawdzamy, czy lotnisko startowe równe jest zmiennej start
, oraz czy lotnisko przylotu równe jest zmiennej end
. Jeśli obie wartości są prawdziwe, całe wyrażenie jest prawdziwe, więc wyświetlamy odpowiedni tekst i zmieniamy wartość zmiennej notExists
na false
. Dzięki takiemu zabiegowi, nie wyświetlamy zbędnego tekstu informującego, że lot nie istnieje – już go wcześniej znaleźliśmy.
Przerywanie pętli
Zaprezentowany kod ma istotną wadę – mimo że lot został znaleziony, pętla nie jest przerywana. Możemy wyobrazić sobie, że mamy w kolekcji 100 lotów, a oczekiwany lot znajduje się pod indeksem 0. Znajdujemy go i wykonujemy 99 zbędnych iteracji. W przedstawionym przykładzie, wyszukiwanie można przerwać na dwa sposoby.
Pierwszy z nich to instrukcja break
przerywającą pętlę:

Instrukcja break
przerywa pętlę, co znaczy, że nie wykonają się jej dalsze iteracje. Wykona się natomiast instrukcja if
. break
wpływa jedynie na pętlę, nic poza nią nie zmienia.
Drugi sposób to przerwanie metody za pomocą return
:

Jak widzisz, kod został uproszczony, zniknęła zmienna notExists
– nie jest już potrzebna. return
używaliśmy w metodach, gdzie zwracana była jakaś wartość. Tutaj mamy metodę void, ale ciągle możemy użyć return
– metoda zostanie przerwana w danym miejscu, nic dalej się nie wykona. Dzięki przerwaniu metody mogliśmy usunąć instrukcję if
, gdyż nie była nam już potrzebna.
Rozbudowa metody
Do tej pory sprawdziliśmy, czy dany lot istnieje w bazie. Przejdźmy krok dalej w celu wyświetlenia wszystkich lotów z danego miasta startowego. Masz pomysł, jak to zrobić?
W klasie FlightDatabase
dodajemy metodę getFlightsFromCity(String city)
:

Podobnie jak wcześniej, przechodzimy po wszystkich obiektach typu Flight
z listy i sprawdzamy, czy lotnisko startowe jest takie samo, jak przekazany argument (metoda equals
). Następnie wypisujemy informacje o locie poprzez wywołanie metody getDetails()
na obiekcie typu Flight
.
Nie zapomnij o przetestowaniu metody! Wystarczy, że dodasz wywołanie nowej metody w metodzie Main
:

W efekcie wyświetliły się informacje o znalezionych lotach. A co się stanie, jeśli wpiszemy miasto, które nie istnieje w naszej bazie? Nic się nie stanie. Jest to dość spory problem, ponieważ nie mamy żadnej informacji, że coś poszło nie tak. Dobrą praktyką w pisaniu aplikacji jest informowanie użytkownika, jeżeli wystąpił jakiś błąd. Spróbujmy więc dodać obsługę takiego wyjątku do naszej aplikacji:

Użyliśmy podobnego rozwiązania jak w metodzie sprawdzającej, czy lot istnieje. Rozwiązanie proste i skuteczne – i o to chodzi! Jeśli znajdziemy przynajmniej jeden lot z podanego miasta, ustawiamy wartość zmiennej isNotFound
na false
, przez co wiadomość w instrukcji if
poza pętlą nie wyświetli się.
Dodajemy nową metodę
Teraz Twoja kolej. Mamy już metodę, która wyświetla wszystkie loty z danego miasta. Teraz samodzielnie napisz metodę, która wyświetli loty do danego miejsca.
To zadanie nie powinno sprawić Ci problemu! Możesz sprawdzić swoje rozwiązanie poniżej:

Jak widzisz, metody szukające lotów z danego miasta i do miasta są bardzo podobne. W programowaniu często staramy się tworzyć kod, który będzie mógł być wykorzystany wiele razy. Znaczy to, że jeśli można uwspólnić jakiś fragment, należy to zrobić. W naszym przypadku powtarzają się dwie operacje: wyświetlanie lotów i wyświetlanie informacji o braku lotów. Warto to połączyć w jedną metodę, która wykona oba fragmenty:

Przeanalizujmy powyższy kod. Metoda przyjmuje listę lotów. Następnie sprawdzane jest, czy lista jest pusta, jeśli tak, wyświetlana jest stosowna informacja. Dalej, przechodzimy po wszystkich elementach z otrzymanej listy, pobieramy każdy element i wyświetlamy go. Jest to część wspólna dla wyszukiwania lotów z miasta i do miasta. Musimy jednak zmodyfikować metody wyszukujące, aby zwracały listę. Spróbuj zrobić to samodzielnie, podążając za wskazówkami:
- utwórz pustą listę w metodzie wyszukującej loty,
- przejdź po wszystkich lotach, a jeśli warunek jest spełniony, dodaj lot do nowoutworzonej listy,
- zwróć listę z metody.
Zmodyfikowane metody powinny wyglądać następująco:

Czy wiesz, jak wszystko “spiąć” w całość i wywołać, aby otrzymać prawidłowy wynik?

Po wywołaniu metody getFlightsFromCity
otrzymujemy wynik w postaci listy, którą przypisujemy do nowej zmiennej o nazwie fromCity
. Listę przekazujemy do metody displayFlights
, która potrafi ją odpowiednio wyświetlić. Analogicznie z drugą kolekcją.
W metodzie Main
powtarzają się dwie operacje: pobieranie lotów i ich wyświetlanie. To też możemy uwspólnić. Utwórz dwie metody displayFlightsFromCity(String city)
oraz displayFlightsToCity(String city)
i z przygotowanych wcześniej metod utwórz nową implementację.
Wynik znajduje się poniżej:

Wywołanie:

Działa dokładnie tak samo, ale sposób się różni. Możliwe, że zastanawiasz się, czemu raz używana jest konstrukcja nazwaObiektu.nazwaMetody
, a innym razem po prostu wywołujemy metodę bez obiektu. Różnica zależy od tego, gdzie wywołuje się metodę. getFlightsFromCity
możemy wywołać wewnątrz displayFlightsFromCity
, ponieważ znajduje się ona w tej samej klasie FlightDatabase
. Jeśli wywołujemy displayFlightsFromCity
w Main
, to musimy użyć obiektu, ponieważ nie jesteśmy już w klasie FlightDatabase
. Mówiąc inaczej, wywołując metodę z obiektu, ona sama wie, co musi zrobić, aby wykonać daną czynność. Można to porównać z telefonem komórkowym. Chcemy zadzwonić do pierwszego kontaktu na liście, powiedzmy, że będzie to Adam. Wybieramy "Zadzwoń do Adam". To cała akcja, jaką wykonujemy na obiekcie telefonu komórkowego. Pod spodem ukryty jest szereg innych operacji, których nie widzimy. Wykonywane są one wewnątrz telefonu, ale nie znaczy to, że jest to inny obiekt.
Powoli zbliżamy się do końca dzisiejszej części Wyzwania. Przemyśl jeszcze raz sposób wywoływania metod: kiedy musi być użyty obiekt, a kiedy nie. Rozejrzyj się wokół siebie, z pewnością znajdziesz wiele podobnych przykładów!
Zadanie domowe
Napisz metodę getCities
, która zwróci (ważne – zwróci, a nie tylko wyświetli) listę z wszystkimi miastami bez duplikatów. Będą Ci potrzebne dwie, nieznane do tej pory rzeczy:
- Negację wartości boolean – odwrócenie wartości. Przykład:
System.out.println(!true)
wyświetlifalse
. Operator!
odwraca wartość typu boolean,true
zamienia nafalse
,false
zamienia natrue
. - Na obiekcie typu
ArrayList
można wywołać metodęcontains(Object o)
. Do metody przekazujemy wartość, w odpowiedzi zwraca nam wartość typu boolean, która mówi, czy przekazany element znajduje się w danej liście.
Powodzenia!