Sierpień 8, 2018

Klasy – ciąg dalszy

Klasy – ciąg dalszy

W poprzedniej lekcji nauczyłeś się tworzyć klasy – deklarować i inicjalizować w nich pola oraz używać konstruktorów: bezparametrowego i parametrowego.

Możliwe, że już zauważyłeś, że konstruktor parametrowy zazwyczaj jest przydatniejszy. Dzięki niemu możemy wprowadzić ustalone wartości do obiektu podczas jego tworzenia – no i mamy wtedy gotową paczuchę z naszymi wartościami. 😉

Sprawę pól w klasie mamy obcykane – czas na pisanie i używanie metod w klasie.

Ze względu, że takie klasy jak User są nazywane modelami lub entity – ich zadaniem jest po prostu przechowywanie informacji. W takim razie stworzymy sobię drugą klasę.

UserService – tak własnie nazywa się klasa, którą sobie stworzymy. Jak nazwa mówi będzie to jakiś serwis userowy – czyli będzie on wykonywał pewne operację na obiektach typu User.

Zazwyczaj w programowaniu trzyma się pewnych konwencji nazewniczych: serwisy własnie mają sufiks Service, walidatory Validator itd. – aby móc w prostych sposób rozróżnić odpowiedzialność klas.

Po co nam ten serwis…

Serwis będzie przechowywał w sobie metody odpowiedzialne za operację na Userze – spójrz, że kolejny raz grupujemy wspólne zagadnienia w jedną klasę!

Czy nie można byłoby tego zrobić w klasie User?

Nikt Ci tego nie zabroni – lecz jest to bardzo brzydkie zachowanie i nie wolno tak robić!

Single Responsibility Principle

Jednym z punktów dobrych praktyk jest właśnie Single Responsibility Principle – czyli ZASADA JEDNEJ ODPOWIEDZIALNOŚCI.

Podkreślam JEDNEJ!

Dlatego nasze klasy powinny być oddzielona – każda powinna być odpowiedzialna za określony cel.

Klasa User jest odpowiedzialna za przechowywanie informacji o Userze.

Klasa UserService będzie odpowiedzialna za przeróżne operację na Userze.

Skoro wiesz po co ten cały cyrk to możemy przejść do tworzenia naszej klasy. 😉

User

Do operowania na obiektach User w UserService oczywiście potrzebujemy klasy User.

Klasa User jest taka sama jak ta, którą stworzyliśmy w poprzedniej lekcji.

public class User {
    String login;
    String password;
    String email;
    int age;
    public User() {
        login = "login";
        password = "pass";
        email = "mail@example.com";
        age = 0;
    }
    public User(String login, String password, String email, int age) {
        this.login = login;
        this.password = password;
        this.email = email;
        this.age = age;
    }
}

Mając klasę User możemy przejść dalej. 😉

UserService

Wystartujemy oczywiście z pustej klasy – którą potrafisz już tworzyc.

public class UserService {
}

Zazwyczaj serwisy nie są zbyt bogate w pola – mógłaby ona zawierać tylko jedno pole z obiektem odpowiedzialnym za kontakt z bazą danych.

Ze względu, że u nas nie mamy bazy danych to stwórzmy po prostu tablicę dla czterech userów – przypuśćmy, że to jest nasza baza danych. 😉

User [] users = new User[4]

Stworzymy tylko konstruktor bezparametrowy – a w nim zainicjalizujemy czterech userów, aby mieć na czym pracować – choć w realnym projekcie to nie powinno tak wyglądać. Robimy to tylko w celach edukacyjnych. 😉

public UserService() {
    users[0] = new User("admin", "pass", "mail", 32);
    users[1] = new User("user", "pass", "mail", 62);
    users[2] = new User("pablo", "pass", "mail", 52);
    users[3] = new User("esco", "pass", "mail", 21);
}

Skoro mamy już przygotowany podstawowy szablon naszej klasy to czas stworzyć jakieś metody – my stworzymy dwie.

getUserByLogin

Już wiesz jakie zadanie będzie miała ta metoda?

Jeżeli nie to posłuchaj – będzie ona miała za zadanie wyszukać usera po podanych loginie.

Powinna zapalić się lampka w głowie – getUser – czyli musi zwracać User. 

Kolejnym punktem jest kolejny człon metody – ByLogin – czyli po loginie – czyli musimy go podać przez argument do funkcji. 😉

No i mamy taką sygnaturę funckcji:

public User getUserByLogin(String login) {
}

Czas na ciało funkcji – musimy przeszukać całą tablicę (naszą małą bazę danych 😉 ), aby znaleźć usera o podanym loginie.

Wykorzystamy do tego pętle – najłatwiej będzie wykorzystać foreach.

for (User user:users) {
}

Dzięki niej przejdziemy po wszystkich userach tablicy, potrzebny nam teraz warunek, który będzie zwracał prawdę, gdy loginy będą równe. 😉

boolean isSameLogin = user.login.equals(login);

Chwilę się zatrzymamy -login wyciągane usera zawiera się w: user.login – jest to login usera z tablicy.

Na tym loginie – na obiekcie typu String – wywołujemy wbudowaną metodę equals – która działa bardzo podobnie jak operator == – choć działa lepiej. O czym można poczytać w tym artykule.

Metoda equals przyjmuje jako argument drugi obiekt typu String – i metoda equals zwraca prawdę jeżeli podany argument jest równy obiektowi, na którym wywoływana jest metoda equals.

Jeżeli, więc loginy są równe to możemy zwrócić znalezionego Usera z metody.

if (isSameLogin) {
    return user;
}

Używając w pętli instrukcji return – automatycznie przerwiemy działanie metody i zwrócimy – wyplujemy – znalezionego usera.

Jednak co jeśli nie znajdziemy?

Jednym ze sposobów jest zwrócenie null.

return null;

Czym jest null?

Null jest tak naprawdę niczym – nie wskazuje na żaden obszar w pamięci. Null może być zwrócony za każdym razem, gdy oczekujemy jakiegoś obiektu.

Czyli nie możemy zwrócić w takiej metodzie:

public int getInt() {
  return null;
}

Taka metoda nie zadziała, ponieważ int nie jest obiektem – tylko typem prymitywnem.

Za to zwracając Stringa:

public String getString() {
  return null;
}

To można już zastosować taki myk – ponieważ String jest obiektem.

W Javie jest prosta zasada – typy pisane z małej litery są typami prymitywnymi np. int, char, short itd. – typy obiektowe są pisane z dużej litery.

Skoro wiemy czym jest null to podsumujmy jak wygląda nasza metoda:

public User getUserByLogin(String login) {
    for (User user:users) {
        boolean isSameLogin = user.login.equals(login);
        if (isSameLogin) {
            return user;
        }
    }

    return null;
}

Użyjmy jej teraz w main!

Testowanie UserService.getUserByLogin

Stwórzmy na początku obiekt typu UserService w main:

public class Main {

    public static void main(String[] args) {
        UserService userService = new UserService();
    }
}

Wykorzystajmy teraz naszą metodę do znalezienia usera o loginie: pablo.

User user = userService.getUserByLogin("pablo");

Wynik – czyli Usera lub null przypisujemy do obiektu user.

Wypiszmy na ekran co się znalazła nasza metoda.

System.out.println("Znaleziono usera: " + user.login + " " + user.email);

Po uruchomieniu:

Znaleziono usera: pablo mail

Czyli nasza metoda działa wyśmienicie.

Sprawdźmy jej działanie, gdy nie znajdzie użytkownika:

User kamil = userService.getUserByLogin("kamil");

Oraz wypiszmy tak samo na ekran konsoli:

Exception in thread "main" java.lang.NullPointerException
  at pl.maniaq.Main.main(Main.java:12)
Znaleziono usera: pablo mail

Błąd w 12 linii – co tam jest?

System.out.println("Znaleziono usera: " + kamil.login + " " + kamil.email);

Czyli chodzi pewnie o obiekt kamil.

Został wyrzucony błąd: NullPointerException – znowu ten null.

User nie został znaleziony, więc zostały zwrócony null – czyli nic! Nie jest on żadnym obiektem – nie możemy, więc na nim wywołać login i email!

Zróbmy zabezpieczenie przy użyciu if.

if (kamil != null) {
    System.out.println("Znaleziono usera: " + kamil.login + " " + kamil.email);
} else {
   System.out.println("Nie znaleziono usera."); 
}

Mamy zabezpieczenie – jeżeli nasz obiekt kamil nie jest nullem – to dopiero wtedy wyświetlamy informację. W przeciwnym razie nie używamy go – bo go nie ma!

Znaleziono usera: pablo mail
Nie znaleziono usera.

No i teraz wszystko działa poprawnie. 😉

sumUsersAge()

Stwórzmy jeszcze jedną metodę: liczącą sumę wieku wszystkich użytkowników.

public int sumUsersAge() {
}

Aby przejść po wszystkich użytkownikach potrzebujemy pętli – a do sumowania wieku zmiennej. 😉

int sumAge = 0;
for (User user:users) {
}

No i w pętli sumujemy – na koniec zwracamy sumę. I tak wygląda nasza cała metoda:

public int sumUsersAge() {
    int sumAge = 0;
    for (User user:users) {
        sumAge += user.age;
    }
    return sumAge;
}

Przetestujmy jej działanie:

int sumAge = userService.sumUsersAge();
System.out.println("Suma wieku wszystkich użytkowników to: " + sumAge);

Po uruchomieniu:

Suma wieku wszystkich użytkowników to: 167

No i wszystko ponownie działa poprawnie. 😉

Podsumowanie

Po pierwsze lekcji powinieneś już potrafić tworzyć proste klasy – z polami i konstruktorem. Po tej lekcji powinieneś potrafić tworzyć w tych klasach metody oraz potrafić je wywoływać. Sprawdź się wykonując poniższe zadania. 😉

  1. Stwórz klasę NumberService – będzie ona odpowiedzialna za operację na liczbach, które będą zapisane w polu klasy w tablicy – tak samo jak w lekcji Userzy. Tablica wygląda tak: int [] numbers = {10, 2, 3, 85, 23, 491, 23, 412, 42, 41, 22, 25}; Napisz następujące metody w klasie:
      • getCountNumbers() – zwracająca liczbę liczb
      • countNumbersHigherThan(int number) – zwraca ilość liczb większy od podanej jako argument
      • countNumbersLowerThan(int number) – zwraca ilość liczb mniejszych od podanej jako argument
      • sumNumbers() – zwraca sumę liczb
      • sortNumbers() – sortuje liczby
      • displayNumbers() – wyświetla wszystkie liczby
  2. Stwórz klasę Human – w której będziesz przechowywał imię, wzrost i wagę człowieka – inicjalizowane przez konstruktor parametrowy.
  3. Stwórz klasę HumanService – która będzie w sobie zawierała tablicę 5 obiektów typu Human ( z zadania drugiego). Na tablicy tych obiektów wykonuj operację przy użyciu metod. Stwórz te metody:
    • countHumanTallerThan(int height) – zwraca ilość ludzi mających większy wzrost niż podany jako argument
    • countHumanLowerThan(int height) – zwraca ilość ludzi mających mniejszy wzrost niż podany jako argument
    • countWeights() – zwraca łączną wagę wszystkich ludzi
    • countHeights() – zwraca łączny wzrost wszystkich ludzi
    • getCountHumans() – zwraca liczbę ludzi na liście
    • getHumanByName(String name) – zwraca usera, który ma tak samo na imię jak imię podane jako argument

Wszystkie metody przetestuje w klasie Main – a do sortowania liczb możesz wykorzystać sortowanie bąbelkowe – najprostsze sortowanie. 😉

Rozwiązania zadań możesz znaleźć tutaj.

Jeżeli udało Ci się rozwiązać wszystkie zadania to czas przejść do następnej lekcji. 😉