Ta strona wykorzystuje pliki cookie w celu prezentacji dopasowanych dla Ciebie treści. Możesz włączyć/wyłączyć obsługę plików cookies w swojej przeglądarce.

Dowiedz się więcej

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:

image

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ę:

image

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:

image

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):

image

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:

image

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:

image

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:

image

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:

image

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:

image

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

image

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:

image

Wywołanie:

image

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:

  1. Negację wartości boolean – odwrócenie wartości. Przykład: System.out.println(!true) wyświetli false. Operator ! odwraca wartość typu boolean, true zamienia na false, false zamienia na true.
  2. 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!