【问题标题】:Builder Pattern in KotlinKotlin 中的构建器模式
【发布时间】:2017-08-10 04:57:08
【问题描述】:

我是 kotlin 世界的新手。我有一个用 Java 编写的现有构建器,并且想将其转换为 Kotlin,因为我正在将项目迁移到 Android 中的 kotlin。但是,Android Studio 内置工具似乎有一些错误,则转换后的代码无法编译。它显示我的UserBuilder 类中的变量无法访问。

这是来自tutorial的Java代码

public class Person {
    private final String firstName; // required
    private final String lastName; // required
    private final int age; // optional
    private final String phone; // optional
    private final String address; // optional

    private Person(UserBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public static class UserBuilder {
        private final String firstName;
        private final String lastName;
        private int age;
        private String phone;
        private String address;

        public UserBuilder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

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

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

        public Person build() {
            return new Person(this);
        }

    }
}

自动转换的kotlin代码:

class Person private constructor(builder: UserBuilder) {
    val firstName: String // required
    val lastName: String // required
    val age: Int // optional
    val phone: String // optional
    val address: String // optional

    init {
        //cannot access the variables, they are private in UserBuilder
        this.firstName = builder.firstName  
        this.lastName = builder.lastName   
        this.age = builder.age
        this.phone = builder.phone
        this.address = builder.address
    }

    class UserBuilder(private val firstName: String, private val lastName: String) {
        private var age: Int = 0
        private var phone: String? = null
        private var address: String? = null

        fun age(age: Int): UserBuilder {
            this.age = age
            return this
        }

        fun phone(phone: String): UserBuilder {
            this.phone = phone
            return this
        }

        fun address(address: String): UserBuilder {
            this.address = address
            return this
        }

        fun build(): Person {
            return Person(this)
        }

    }
}

更新

class Person private constructor(builder: UserBuilder) {
    val firstName: String // required
    val lastName: String // required
    val age: Int // optional
    val phone: String? // optional
    val address: String? // optional

    init {
        this.firstName = builder.firstName
        this.lastName = builder.lastName
        this.age = builder.age
        this.phone = builder.phone
        this.address = builder.address
    }

    class UserBuilder(internal val firstName: String, internal val lastName: String) {
        internal var age: Int = 0
        internal var phone: String? = null
        internal var address: String? = null

        fun age(age: Int): UserBuilder {
            this.age = age
            return this
        }

        fun phone(phone: String): UserBuilder {
            this.phone = phone
            return this
        }

        fun address(address: String): UserBuilder {
            this.address = address
            return this
        }

        fun build(): Person {
            return Person(this)
        }

    }
}

【问题讨论】:

  • 这是一个糟糕的构建器设计,一个与另一个耦合。
  • 如果你能提供一个更好的想法,我很想看看它在 Kotlin 中是如何工作的,因为我是 Kotlin 世界的新手。
  • 对不起,我在移动设备上,我认为 kcoppock 的回答可以满足您的需求。我说的是:“User不应该依赖于UserBuilder”,:)

标签: kotlin


【解决方案1】:

鉴于它是 Kotlin,您实际上可以使用数据类使这变得简单得多。

data class User(val firstName: String, 
        val lastName: String,
        val age: Int = 0,
        val phone: String? = null,
        val address: String? = null)

就是这样!前两个参数是必需的,但如果要指定更多,只需按名称指定即可:

val user = User("John", "Doe", phone = "555-1212")

无需构建器!

【讨论】:

  • 嗨,感谢您的帮助。我知道有对 kotlin 的可选参数支持,但我仍然希望拥有 builder,因为我的 kotlin 库将在 Java 中使用。
  • 这并不能真正取代建造者模式。
  • @Ascorbin 取决于您需要构建器的目的。如果您需要捕获构建器引用并直接使用它,那么这可能不适合您。如果您只是使用构建器从 Java 端提供默认值和/或简化构造函数,那么这实际上是一种替代。
  • 还可以查看android.jlelse.eu/writing-java-friendly-kotlin-code-c408b24fb4e,它展示了如何使用@JvmOverloads 构造函数来生成Java 中需要的构造函数的每个排列,如果您可以从Java Builder 更改为只调用一个单个构造函数。
【解决方案2】:

只需像以前一样进行简单的转换即可。

现在您将看到 UserBuilder 将具有 internal 访问修饰符,只需删除它并添加 public 或任何您的要求。


编辑1

好的,internal 关键字不在您的 Kotlin 代码中,我在检查您的代码时忽略了这一点。

所以现在你需要做的是在 Person 类中创建一个对象并将你的 UserBuilder 类放在那里。

检查以下代码:

class Person private constructor(builder: Builder.UserBuilder) {
    val firstName: String // required
    val lastName: String // required
    val age: Int // optional
    val phone: String // optional
    val address: String // optional

init {
    //cannot access the variables, they are private in UserBuilder
    this.firstName = builder.firstName  
    this.lastName = builder.lastName   
    this.age = builder.age
    this.phone = builder.phone
    this.address = builder.address
}
object Builder {
class UserBuilder(private val firstName: String, private val lastName: String) {
    private var age: Int = 0
    private var phone: String? = null
    private var address: String? = null

    fun age(age: Int): UserBuilder {
        this.age = age
        return this
    }

    fun phone(phone: String): UserBuilder {
        this.phone = phone
        return this
    }

    fun address(address: String): UserBuilder {
        this.address = address
        return this
    }

    fun build(): Person {
        return Person(this)
    }

}
}
}

这应该像在您的 Java 代码中一样工作。

希望对你有帮助。

【讨论】:

  • 您的解决方案似乎仍然不适合我。它显示了相同的可访问性错误。我最终将UserBuilder 中的实例变量更改为内部Person 以访问它。我忘了说这个方法会被其他模块调用。
  • 我已经检查过了,上面的代码可以从不同的模块正常工作。只需重新检查您是否遗漏了什么。
【解决方案3】:
class Person private constructor(builder: UserBuilder) {
val firstName: String // required
val lastName: String // required
val age: Int // optional
val phone: String // optional
val address: String // optional

init {
    this.firstName = builder.firstName  
    this.lastName = builder.lastName   
    this.age = builder.age
    this.phone = builder.phone
    this.address = builder.address
}

class UserBuilder(private val _firstName: String, private val _lastName: String) {
    private var _age: Int = 0
    private var _phone: String? = null
    private var _address: String? = null

    var firstName: String
        get() = this._firstName
    var lastName: String
        get() = this._lastName
    var age: Int = 0
        get() = this._age
    var phone: String? = null
        get() = this._phone
    var address: String? = null
        get() = this._address

    fun age(age: Int): UserBuilder {
        this._age = age
        return this
    }

    fun phone(phone: String): UserBuilder {
        this._phone = phone
        return this
    }

    fun address(address: String): UserBuilder {
        this._address = address
        return this
    }

    fun build(): Person {
        return Person(this)
    }

}

}

以这种方式使用自定义getter可以解决私有访问修饰符问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多