Styczeń 21, 2019

Java pytanie rekrutacyjne: Wieloparametrowe konstruktory

Czy wieloparametrowe konstruktory są poprawne?

Wieloparametrowe konstruktory pod względem składni języka są poprawne, za to występują z nimi inne problemy.

W przypadku, gdy mamy konstruktor z większą ilością parametrów np. cztery oraz większość parametrów jest tego samego typu to zaczynają się problemy. Stwórzmy prosty przykład, najzwyklejsza POJO klasa Usera z 6 polami:

package org.blog;

public class User {
    private Long id;
    private String name;
    private String lastName;
    private String login;
    private String email;
    private String password;

    public User(Long id, String name, String lastName, String login, String email, String password) {
        this.id = id;
        this.name = name;
        this.lastName = lastName;
        this.login = login;
        this.email = email;
        this.password = password;
    }
}

Od razu w oczy rzuca się to, że aż 5 pól jest typu String. Przejdźmy do inicjalizacji obiekty typu User:

User user = new User(1L, "Pablo", "Escabo", "Admin", "pablo@example.com", "admin");

Muszę, że poszło mi to nie najgorzej, ale tylko dlatego, że bardzo pomogło mi IDE – IntelliJ Idea. Bez jego pomocy prawdopodobnie pomyliłbym się w kolejności argumentów, albo musiałbym co chwilę sprawdzać w kolejność.

Builder

Najlepszym rozwiązaniem na powyższy problem jest wzorzec projektowy Builder. Nie będę się tutaj rozpisywał jak go implementować, pokażę tylko jego użycie na klasie User. W skrócie polega to na ciągłym budowaniu obiektu poprzez wywoływanie metod z wcześniej utworzonego buildera – w momencie, gdy „ułożymy” wszystkie klocki dochodzi do finalnego zbudowania obiektu. Zazwyczaj oznacza to wywołanie metody build.

package org.blog;

public class User {
    private Long id;
    private String name;
    private String lastName;
    private String login;
    private String email;
    private String password;

    private User(Long id, String name, String lastName, String login, String email, String password) {
        this.id = id;
        this.name = name;
        this.lastName = lastName;
        this.login = login;
        this.email = email;
        this.password = password;
    }

    public static class Builder {
        private Long id;
        private String name;
        private String lastName;
        private String login;
        private String email;
        private String password;

        public Builder id(Long id) {
            this.id = id;
            return this;
        }

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public Builder login(String login) {
            this.login = login;
            return this;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder password(String password) {
            this.password = password;
            return this;
        }

        public User build() {
            return new User(id, name, lastName, login, email, password);
        }
    }
}

Oczywiście tworzenie obiektu wygląda teraz całkiem inaczej – na pewno czytelniej.

User user = new User.Builder()
        .id(1L)
        .name("Pablo")
        .lastName("Escabo")
        .login("admin")
        .email("pablo@example.com")
        .password("admin")
        .build();

Jedynym problemem jest oczywiście utrzymywanie klasy Builder – wraz ze zmianą pól w klasie User musimy zmieniać również strukturę klasy Builder. Nie ma się co martwić i na to jest rozwiązanie – wystarczy skorzystać z tzw. Lomboka. Lombok jest biblioteką, która robi za nas wiele podając nad klasą tylko odpowiednią adnotację. Generuje np.:

  • settery i gettery,
  • konstruktory,
  • hashCode i equals,
  • toString,
  • builder.

Czyli te rzeczy, których nie chcemy robić ręcznie. My chcemy pisać logikę biznesową. 😉

Podsumowując najlepiej jest tworzyć konstruktory maksymalnie z 3 parametrami – w przeciwnym wypadku warto korzystać ze wzorca projektowego budowiczny. Oczywiście ta zasada nie dotyczy tzw. beanów w Springu, które są zarządzane przez Spring Container – czyli nie są tworzone poprzez operator new.

Warto wspomnieć, że chcąc używać Lomboka w IntelliJ warto zainstalować sobie plugin do lomboka, abyśmy mogli od razu otrzymywać podpowiedzi o generowanych metodach.

Więcej podobnych zasad czystego kodu możesz znaleźć w książce „Czysty Kod” Roberta C. Martina.