Как реализовать шаблон Builder в Котлине?
Привет я новичок в мире Котлин. Мне нравится то, что я вижу до сих пор и начал думать, чтобы преобразовать некоторые из наших библиотек, которые мы используем в нашем приложении от Java до Kotlin.
эти библиотеки полны Pojos с сеттерами, геттерами и классами Builder. Теперь я погуглил, чтобы найти лучший способ реализовать Строителей в Котлине, но без успеха.
2-е обновление: вопрос в том, как написать Дизайн-Шаблон Builder для простого pojo с некоторыми параметрами в Kotlin? Приведенный ниже код-это моя попытка написать java-код, а затем использовать Eclipse-kotlin-plugin для преобразования в Kotlin.
class Car private constructor(builder:Car.Builder) {
var model:String? = null
var year:Int = 0
init {
this.model = builder.model
this.year = builder.year
}
companion object Builder {
var model:String? = null
private set
var year:Int = 0
private set
fun model(model:String):Builder {
this.model = model
return this
}
fun year(year:Int):Builder {
this.year = year
return this
}
fun build():Car {
val car = Car(this)
return car
}
}
}
8 ответов:
прежде всего, в большинстве случаев вам не нужно использовать builders в Kotlin, потому что у нас есть аргументы по умолчанию и именованные. Это позволяет вам писать
class Car(val model: String? = null, val year: Int = 0)и использовать его вот так:
val car = Car(model = "X")если вы абсолютно хотите использовать строителей, вот как вы могли бы это сделать:
сделать сайт
companion objectне имеет смысла, потому чтоobjects-синглтоны. Вместо этого объявите его как вложенный класс (который является статическим по умолчанию в Котлин).переместите свойства в конструктор, чтобы объект также можно было создать обычным способом (сделать конструктор закрытым, если это не так) и использовать вторичный конструктор, который принимает конструктор и делегирует первичному конструктору. Код будет выглядеть следующим образом:
class Car( //add private constructor if necessary val model: String?, val year: Int ) { private constructor(builder: Builder) : this(builder.model, builder.year) class Builder { var model: String? = null private set var year: Int = 0 private set fun model(model: String) = apply { this.model = model } fun year(year: Int) = apply { this.year = year } fun build() = Car(this) } }использование:
val car = Car.Builder().model("X").builder()этот код может быть сокращен, кроме того, с помощью builder DSL:
class Car ( val model: String?, val year: Int ) { private constructor(builder: Builder) : this(builder.model, builder.year) companion object { inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build() } class Builder { var model: String? = null var year: Int = 0 fun build() = Car(this) } }использование:
val car = Car.build { model = "X" }если некоторые значения обязательны и не имеют значений по умолчанию, вам нужно поместить их в конструктор конструктора, а также в
buildметод, который мы только что определили:class Car ( val model: String?, val year: Int, val required: String ) { private constructor(builder: Builder) : this(builder.model, builder.year, builder.required) companion object { inline fun build(required: String, block: Builder.() -> Unit) = Builder(required).apply(block).build() } class Builder( val required: String ) { var model: String? = null var year: Int = 0 fun build() = Car(this) } }использование:
val car = Car.build(required = "requiredValue") { model = "X" }
поскольку я использую библиотеку Джексона для разбора объектов из JSON, мне нужно иметь пустой конструктор, и я не могу иметь необязательные поля. Также все поля должны быть изменяемыми. Тогда я могу использовать этот хороший синтаксис, который делает то же самое, что и шаблон Builder:
val car = Car().apply{ model = "Ford"; year = 2000 }
Я лично никогда не видел строителя в Котлине, но, может быть, это только я.
все проверки нужно происходит в
initблок:class Car(val model: String, val year: Int = 2000) { init { if(year < 1900) throw Exception("...") } }здесь я взял на себя смелость предположить, что вы на самом деле не хотели
modelиyearбыть переменчивым. Также Эти значения по умолчанию, кажется, не имеют смысла, (особенноnullнаname), но я оставил один для демонстрационных целей.Мнение: Шаблон построителя используется в Java как значит жить без именованных параметров. В языках с именованными параметрами (например, Kotlin или Python) рекомендуется иметь конструкторы с длинными списками (возможно, необязательными) параметров.
Я видел много примеров, которые объявляют дополнительных средств в качестве строителей. Лично мне такой подход нравится. Сохраните усилия, чтобы написать строителей.
package android.zeroarst.lab.koltinlab import kotlin.properties.Delegates class Lab { companion object { @JvmStatic fun main(args: Array<String>) { val roy = Person { name = "Roy" age = 33 height = 173 single = true car { brand = "Tesla" model = "Model X" year = 2017 } car { brand = "Tesla" model = "Model S" year = 2018 } } println(roy) } class Person() { constructor(init: Person.() -> Unit) : this() { this.init() } var name: String by Delegates.notNull() var age: Int by Delegates.notNull() var height: Int by Delegates.notNull() var single: Boolean by Delegates.notNull() val cars: MutableList<Car> by lazy { arrayListOf<Car>() } override fun toString(): String { return "name=$name, age=$age, " + "height=$height, " + "single=${when (single) { true -> "looking for a girl friend T___T" false -> "Happy!!" }}\nCars: $cars" } } class Car() { var brand: String by Delegates.notNull() var model: String by Delegates.notNull() var year: Int by Delegates.notNull() override fun toString(): String { return "(brand=$brand, model=$model, year=$year)" } } fun Person.car(init: Car.() -> Unit): Unit { cars.add(Car().apply(init)) } } }Я еще не нашел способ, который может заставить некоторые поля инициализироваться в DSL, например, показывать ошибки вместо исключения. Дайте мне знать, если кто-нибудь знает.
Я бы сказал, что шаблон и реализация остаются почти такими же в Котлине. Иногда вы можете пропустить его благодаря значениям по умолчанию, но для более сложного создания объектов конструкторы по-прежнему являются полезным инструментом, который нельзя пропустить.
вы можете использовать дополнительный параметр в kotlin пример:
fun myFunc(p1: String, p2: Int = -1, p3: Long = -1, p4: String = "default") { System.out.printf("parameter %s %d %d %s\n", p1, p2, p3, p4) }затем
myFunc("a") myFunc("a", 1) myFunc("a", 1, 2) myFunc("a", 1, 2, "b")
для простого класса вам не нужен отдельный конструктор. Вы можете использовать необязательные аргументы конструктора, как описано Кириллом Рахманом.
Если у вас есть более сложный класс, то Kotlin предоставляет способ создания Groovy style Builders/DSL:
вот пример:
class Foo private constructor(@DrawableRes requiredImageRes: Int, optionalTitle: String?) { @DrawableRes @get:DrawableRes val requiredImageRes: Int val optionalTitle: String? init { this.requiredImageRes = requiredImageRes this.requiredImageRes = optionalTitle } class Builder { @DrawableRes private var requiredImageRes: Int = -1 private var optionalTitle: String? = null fun requiredImageRes(@DrawableRes imageRes: Int): Builder { this.intent = intent return this } fun optionalTitle(title: String): Builder { this.optionalTitle = title return this } fun build(): Foo { if(requiredImageRes == -1) { throw IllegalStateException("No image res provided") } return Foo(this.requiredImageRes, this.optionalTitle) } } }
Comments