Комментарии 12
А в этом языке сейчас нельзя в конструктор передать сразу дата объект с опциональными полями вместо того чтобы вызывать кучу методов?
Я просто интересуюсь просто я давно на джаве не пишу. Сижу на другом языке где у объектов позволены опциональные поля. Поэтому о патерне билдер давно не слышал потому что вместо вот этих методов можно просто в конструктор передать конфигурационный объект только с теми полями которые нужны.
Можно, конечно. Также можно наделать кучу конструкторов для инициализации только нужных полей. А можно использовать Builder. Все зависит от задачи.
Билдер удобен и тем, что он даёт больше гарантий компиляции. При добавлении нового поля и изменения конструктора предыдущий код (использующий этот конструктор) перестанет компилироваться даже если это поле опционально. С билдером такой проблемы не будет.
Мой комментарий - не критика языка, а критика моего предыдущего комментария ))
простите, я забыл что в Java нет записи объектов в виде литералов ))
Эти литералы по сути и убили для меня паттерн Builder в языках, на которых я пишу
потому что можно писать вот так и не нужно париться с конструкторами
или в Java все-таки появились литералы и я давно не обновлял кодекс?
вообще опциональные поля и синтаксические проверки в JS/TS убили море паттернов, которые были просто компенсаторами языковых конструкций
const someConfig = {
name: 'Vasya',
age: 14,
work: undefined, // эту строчку можно вообще не писать как и миллион других
}
Java хороша в миллионе мест, но боже, как же мне грустно, что в ней не хватает некоторых декларативных вещей, как эти литералы объектов
По сути литералы объектов - это удобный синтаксис для одноразовых объектов
PS: проверка типов на этапе компиляции в TypeScript есть, а я пишу только на нем, поэтому классический Builder я не видел уже со времен динозавров
Раньше писал на java и теперь рад что перешёл kotlin, т.к. больше не приходится писать такой boilerpalte код. Вероятность ошибок снижается и код становится более читаемым.
Вот аналог вышеописанного Builder (исключая проверки) для класса Cat на Kotlin:
data class Cat(
val name: String,
val color: String = "Grey",
val age: Int = 0,
val weight: Double = 0.0,
val isFluffy: Boolean = false,
val meowVolume: Int = 1,
val favoriteFoods: List<String> = emptyList()
)
И вызовы этого класса разными параметрами:
val cat1 = Cat("Vasiliy")
val cat2 = Cat(name = "Barsik", age = 2, favoriteFoods = listOf("Fish", "Chicken"))
Имя является обязательным параметром конструктора, остальные параметры если не будут указаны, то будет использовано значение по умолчанию. Также toString переопределяется автоматически т.к. это data класс. Мне кажется, что преимущество очевидно.
исключая проверки
А если проверки будут нужны? Можете код на Котлине и для этого случая привести? Просто чтобы сравнить идентичные по функциональности вещи.
Я вижу как минимум два варианта.
Использовать блок инициализации init, который будет вызываться при создании объекта и проверять свойства:
data class Cat(
val name: String,
val age: Int = 0
) {
init {
require(name.isNotBlank()) { "Name cannot be null or empty." }
require(age in 0..30) { "Age must be between 0 and 30." }
}
}
Второй вариант - использовать готовую библиотеку валидации Hibernate Validator:
data class Cat(
@field:NotBlank(message = "Name cannot be null or empty.")
val name: String,
@field:Range(min = 0, max = 30, message = "Age must be between {min} and {max}.")
val age: Int = 0
)
Init не подходит в случаях когда для промежуточного состояния используется тот же самый объект.
Hibernate Validator - это дополнительная зависимость + для кастомой валидации будет многословна.
В сумме разница с Java не принципиальная получается.
Init не подходит в случаях когда для промежуточного состояния используется тот же самый объект.
Если для создания объекта необходимо передавать класс Cat - то такая возможность есть. Валидация в init также будет работать.
constructor(cat: Cat) : this(
name = cat.name,
age = cat.age
)
В сумме разница с Java не принципиальная получается.
По мне так разница большая получается, в Kotlin весь необходимый функционал встроен в возможности языка. Каждый раз в Java применять шаблон Builder для обычного POJO - такое себе.
Не понял для чего вся эта "шляпа", похоже на удаление гланд... сами знаете как. Потом кто-то будет говорить, что Java - это очень сложно, слишком много букв.
Barsik: коричневый кот, не особо пушистый, весит 6.2 кг и любит курицу.
Барсик не любит курочку. Барсик любит говядину. По крайней мере так следует из вашего кода.
Cat barsik = new Cat.Builder("Barsik")
...
.addFavoriteFood("Beef").build();
Интересно, а почему не record?
Ну и валидация в сеттерах билдера, а не хотябы внутри `.build()` - это дорога в спагетти ад.
Если надо провалидировать user-input - лучше пользоваться Bean Validation, чем писать чеки вручную непонятно где.
public record Cat(
@NotNull(message = "Name cannot be null")
String name,
@Min(value = 0, message = "Age should not be less than 0")
@Max(value = 30, message = "Age should not be greater than 30")
int age,
...
) {
private Cat(Cat.Builder builder) {
// ИМХО так лучше так не делать, а передавать все параметры в билдере честно
// потому-что меньше кода -> лучше код
// но это скорее вопрос вкуса
...
}
public static class Builder {
...
public Cat build() {
return validator.validate(new Cat(this)); // throws exception if validation failed
}
}
}
(код сверху это не точный пример того как надо использовать 'jakarta.validation', просто пример упрощенный для наглядности)
Паттерн Builder в Java на котиках