Javanese Online

Builder

Если все поля класса финальные (неизменяемые), единственный способ создать экземпляр — это вызов конструктора.

public class User {

    private final String name;
    private final String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
}
new User("John", "john@riseup.net");

Когда полей станет много, вызывать такой конструктор будет проблематично, т. к. в Java нет ни именованных параметров, ни значений параметров по умолчанию.

public class User {

    private final String name;
    private final String email;
    private final String country;
    private final String city;
    private final String street;
    private final String house;

    public User(String name, String email, String country, String city, String street, String house) {
        this.name = name;
        this.email = email;
        this.country = country;
        this.city = city;
        this.street = street;
        this.house = house;
    }
}
new User(/* ой-ой */);

В качестве решения проблемы большого конструктора некоторые используют билдеры:

public class UserBuilder {
    private String name;
    private String email;
    private String country;
    private String city;
    private String street;
    private String house;

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

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

    public UserBuilder setCountry(String country) {
        this.country = country;
        return this;
    }

    public UserBuilder setCity(String city) {
        this.city = city;
        return this;
    }

    public UserBuilder setStreet(String street) {
        this.street = street;
        return this;
    }

    public UserBuilder setHouse(String house) {
        this.house = house;
        return this;
    }

    public User createUser() {
        return new User(name, email, country, city, street, house);
    }
}
new UserBuilder()
        .setName("John")
        .setEmail("john@riseup.net")
        .setCountry("Russia")
        .setCity("Moscow")
        .setStreet("The Red Square")
        .setHouse("somewhere")
        .createUser();

У такого подхода есть единственный плюс — у сеттеров (wither'ов) есть имена.

Минусы

Нормальное решение: уменьшить объект, чтобы снова можно было использовать конструктор.

public class User {
    private final String name;
    private final String email;
    private final Address address;

    public User(String name, String email, Address address) {
        this.name = name;
        this.email = email;
        this.address = address;
    }

}

public class Address {
    private final String country;
    private final String city;
    private final String street;
    private final String house;

    public Address(String country, String city, String street, String house) {
        this.country = country;
        this.city = city;
        this.street = street;
        this.house = house;
    }

}
User user = new User(
        "John",
        "john@riseup.net",
        new Address(
                "Russia", "Moscow", "The Red Square", "somewhere"
        )
);

Именованные параметры

В качестве именованных параметров в билдерах используются имена сеттеров. Это помогает сделать читаемым код наподобие этого:

GeoPosition position = new GeoPosition(database, input.latitude(), input.longitude());
Address address = new AddressBuilder()
    .setCountry(position.country())
    .setCity(position.city())
    .setStreet(position.street())
    .setHouse(input.isHouseSpecified() ? input.house() : position.house())
    .createAddress();

Когда используется конструктор, именованные параметры можно сэмулировать с помощью локальных переменных:

GeoPosition position = new GeoPosition(database, input.latitude(), input.longitude());
final String country = position.country();
final String city = position.city();
final String street = position.street();
final String house = input.isHouseSpecified() ? input.house() : position.house();
Address address = new Address(country, city, street, house);

Здесь задача правильно назвать переменные полностью ложится на плечи того, кто создаёт объект. Зато нет необходимости поддерживать два класса с одинаковыми данными и создавать промежуточный объект.

Комментарии к уроку

Сообщить об ошибке

Javanese.Online в GitHub

Чаты и каналы в Telegram

RSS-лента