如何在Kotlin中实现生成器模式?

嗨,我是Kotlin世界的新手。我喜欢我目前所看到的,并开始考虑将我们在应用程序中使用的一些库从Java转换为Kotlin。

这些库充满了带有setter、getter和Builder类的pojo。现在我已经在谷歌上找到了在Kotlin中实现Builders的最佳方法,但没有成功。

第二次更新:问题是如何写一个建设者设计模式的一个简单的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
}
}
}
113098 次浏览

我想说的是,Kotlin中的模式和实现基本保持不变。由于默认值,您有时可以跳过它,但对于更复杂的对象创建,构建器仍然是一个不可省略的有用工具。

我个人从来没有在Kotlin见过一个建筑工人,但也许只有我这样。

所有需要的验证都发生在init块中:

class Car(val model: String,
val year: Int = 2000) {


init {
if(year < 1900) throw Exception("...")
}
}

在这里,我冒昧地猜测,你并不真的希望modelyear是可更改的。此外,这些默认值似乎没有任何意义,(特别是null对于name),但我留下了一个用于演示。

< >强一个意见: 在Java中使用的构建器模式意味着不需要命名参数。在带有命名形参的语言(如Kotlin或Python)中,使用带有长串(可能是可选的)形参的构造函数是一个很好的实践

首先,在大多数情况下,您不需要在Kotlin中使用构建器,因为我们有默认参数和命名参数。这使你能够写作

class Car(val model: String? = null, val year: Int = 0)

像这样使用它:

val car = Car(model = "X")

如果你绝对想要使用构建器,你可以这样做:

将构建器设置为companion object没有意义,因为__abc1是单例对象。相反,将其声明为一个嵌套类(在Kotlin中默认是静态的)。

将属性移动到构造函数中,这样对象也可以以常规方式实例化(如果不应该,则将构造函数设置为私有),并使用接受构造函数并将其委托给主构造函数的辅助构造函数。代码如下所示:

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").build()

这段代码可以通过使用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" }

因为我使用Jackson库从JSON解析对象,我需要一个空构造函数,我不能有可选字段。而且所有字段都必须是可变的。然后我可以使用这个漂亮的语法,它做的事情与生成器模式相同:

val car = Car().apply{ model = "Ford"; year = 2000 }

对于简单类,不需要单独的构建器。您可以像Kirill Rakhman描述的那样使用可选的构造函数参数。

如果你有更复杂的类,那么Kotlin提供了一种方法来创建Groovy风格的Builders/DSL:

类型安全的建筑商

这里有一个例子:

Github示例-构建器/汇编器

你可以在kotlin中使用可选参数 例子:< / p >

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")

我看到过许多将额外乐趣作为构建者的例子。我个人喜欢这种方法。节省编写构建程序的精力。

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中初始化一些字段,比如显示错误而不是抛出异常。如果有人知道就告诉我。

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)
}


}


}

现在的人们应该检查Kotlin的类型安全的建筑商

使用上述方法创建对象将看起来像这样:

html {
head {
title {+"XML encoding with Kotlin"}
}
// ...
}

vaadin-on-kotlin框架是一个很好的“正在使用”的使用示例,它利用类型安全构建器来组装视图和组件

我在Kotlin中实现了一个基本的Builder模式,代码如下:

data class DialogMessage(
var title: String = "",
var message: String = ""
) {




class Builder( context: Context){




private var context: Context = context
private var title: String = ""
private var message: String = ""


fun title( title : String) = apply { this.title = title }


fun message( message : String ) = apply { this.message = message  }


fun build() = KeyoDialogMessage(
title,
message
)


}


private lateinit var  dialog : Dialog


fun show(){
this.dialog= Dialog(context)
.
.
.
dialog.show()


}


fun hide(){
if( this.dialog != null){
this.dialog.dismiss()
}
}
}

最后

Java:

new DialogMessage.Builder( context )
.title("Title")
.message("Message")
.build()
.show();

芬兰湾的科特林:

DialogMessage.Builder( context )
.title("Title")
.message("")
.build()
.show()

我在一个Kotlin项目中工作,该项目公开了一个由Java客户机使用的API (Java客户机不能利用Kotlin语言结构)。我们必须添加构建器以使它们在Java中可用,所以我创建了@Builder注释:https://github.com/ThinkingLogic/kotlin-builder-annotation -它基本上是Kotlin的Lombok @Builder注释的替代品

一种方法是做以下事情:

class Car(
val model: String?,
val color: String?,
val type: String?) {


data class Builder(
var model: String? = null,
var color: String? = null,
var type: String? = null) {


fun model(model: String) = apply { this.model = model }
fun color(color: String) = apply { this.color = color }
fun type(type: String) = apply { this.type = type }
fun build() = Car(model, color, type)
}
}

使用示例:

val car = Car.Builder()
.model("Ford Focus")
.color("Black")
.type("Type")
.build()

我迟到了。如果我必须在项目中使用Builder模式,我也遇到了同样的困境。后来,经过研究,我意识到这是完全不必要的,因为Kotlin已经提供了命名参数和默认参数。

如果你真的需要实施,Kirill Rakhman的答案是关于如何以最有效的方式实施的可靠答案。你可能会发现另一件有用的事情是https://www.baeldung.com/kotlin-builder-pattern,你可以与Java和Kotlin的实现进行比较和对比

class Person(
val name:String,
val family:String,
val age:Int,
val nationalCode: String?,
val email: String?,
val phoneNumber: String?
) {


// Private constructor
private constructor(builder: Builder) : this (
builder.name,
builder.family,
builder.age,
builder.nationalCode,
builder.email,
builder.phoneNumber
)


// Builder class


// 1 Necessary parameters in Builder class : name , family
class Builder(val name :String,val family :String) {


// 2 Optional parameters in Builder class :
var age: Int = 0
private set
var nationalCode: String? = null
private set
var email: String? = null
private set
var phoneNumber: String? = null
private set


fun age(age: Int) = apply { this.age = age }
fun nationalCode(nationalCode: String) =
apply { this.nationalCode = nationalCode }
fun email(email: String) = apply { this.email = email }
fun phoneNumber(phoneNumber: String) =
apply { this.phoneNumber = phoneNumber }


// 3 Create
fun create() = Person(this)


}
}

存取资料:

val firstPerson = Person.Builder(
name = "Adnan",
family = "Abdollah Zaki")
.age(32)
.email("Adnan9011@gmail.com")
.phoneNumber("+989333030XXX")
.nationalCode("04400XXXXX")
.create()


val secondPerson = Person.Builder(
name = "Foroogh",
family = "Varmazyar")
.create()

我刚刚发现了一个有趣的方法来创建kotlin构建器:

enter image description here

正如你所看到的,moduleBuilder可以用于其他的grafana构建。

代码如下:

class Grafana(
private val module: String,
private val scene: String,
private val action: String,
private val metric: String
) {
companion object {
fun build(module: String, scene: String, action: String, metric: String) =
Grafana(module, scene, action, metric)


val builder = ::build.curriedBuilder()


private fun <P1, P2, P3, P4, R> Function4<P1, P2, P3, P4, R>.curriedBuilder() =
fun(p1: P1) = fun(p2: P2) = fun(p3: P3) = fun(p4: P4) = this(p1, p2, p3, p4)
}


fun report() = Unit
}




val moduleBuilder = Grafana.builder("module")
val scene = moduleBuilder("scene")
val gfA = scene("action")("metric")
gfA.report()


val sceneB = moduleBuilder("sceneB")
val gfB = sceneB("action")("metric")
gfB.report()


val gfC = Grafana.builder("xx")("xxx")("xxxx")("xxxx")
gfC.report()


上面的答案有一点改变和改进

class MyDialog {
private var title: String? = null
private var content: String? = null
private var confirmButtonTitle: String? = null
private var rejectButtonTitle: String? = null


@DrawableRes
private var icon: Int? = null




fun show() {
// set dialog content here and show at the end
}


class Builder {
private var dialog: MyDialog = MyDialog()


fun title(title: String) = apply { dialog.title = title }


fun icon(@DrawableRes icon: Int) = apply { dialog.icon = icon }


fun content(content: String) = apply { dialog.content = content }


fun confirmTitle(confirmTitle: String) = apply { dialog.confirmButtonTitle = confirmTitle }


fun rejectButtonTitle(rejectButtonTitle: String) = apply { dialog.rejectButtonTitle = rejectButtonTitle }


fun build() = dialog
}
}

和使用

MyDialog.Builder()
.title("My Title")
.content("My content here")
.icon(R.drawable.bg_edittext)
.confirmTitle("Accept")
.rejectButtonTitle("Cancel")
.build()
.show()