导读大纲
1.1 本地函数和扩展函数
-
许多开发人员认为,好代码最重要的品质之一就是不重复
- 这一原则甚至有一个专门的名称: 不重复(DRY)
- 但是,当使用 Java 编写代码时,遵循这一原则并不总是轻而易举的事
- 在很多情况下,可以使用IDE的"Extract Method"重构功能
- 将较长的方法分解成较小的部分,然后重复使用这些部分
- 但这可能会使代码更难以理解,因为你最终会发现
- 一个类中有很多小方法,而它们之间没有明确的关系
- 你还可以更进一步,将提取的方法组合到一个内部类中
- 这样就可以保持结构, 但这种方法需要大量的模板
- 这样就可以保持结构, 但这种方法需要大量的模板
-
示例–将用户保存到数据库之前,需要确保User对象包含有效数据
- <1> 这里在验证User字段时, 出现代码重复
class User(val id: Int, val name: String, val address: String)
// ========================== 验证空逻辑出现重复代码
fun saveUser(user: User) {
if (user.name.isEmpty()) { // <1>
throw IllegalArgumentException(
"Can't save user ${user.id}: empty Name")
}
if (user.address.isEmpty()) { // <1>
throw IllegalArgumentException(
"Can't save user ${user.id}: empty Address")
}
// Save user to the database
}
- 这里的重复代码量很小,你可能不想在类中使用一个完整的方法来验证用户
- Kotlin 提供一种更简洁的解决方案:可以将提取的函数嵌套在外层函数中
- 这样,您就能获得所需的结构,而无需任何额外的语法开销
- <1> 声明一个本地函数来验证任何字段
- <2> 调用本地函数来验证特定字段
- Kotlin 提供一种更简洁的解决方案:可以将提取的函数嵌套在外层函数中
fun saveUser(user: User) {
// <1>
fun validate(user: User,
value: String,
fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException(
"Can't save user ${user.id}: empty $fieldName")
}
}
validate(user, user.name, "Name") // <2>
validate(user, user.address, "Address") // <2>
// Save user to the database
}
- 这样看起来更好;验证逻辑没有重复,但仍限制在validate函数的范围内
- 随着项目的发展,如果需要在 User 中添加其他字段
- 可以很容易地添加更多验证
- 将User对象传递给验证函数有点难看,好消息是,这完全没有必要
- 因为局部函数可以访问外层函数的所有参数和变量
- <1> 现在, 你不用重复传递 User 参数
- <2> 可以直接访问外部函数的参数,比如这里直接访问user对象
- 随着项目的发展,如果需要在 User 中添加其他字段
fun saveUser(user: User) {
// <1>
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
// <2>
throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
}
}
validate(user.name, "Name")
validate(user.address, "Address")
// Save user to the database
}
- 你以为这样就结束了??可以进一步封装,将验证逻辑移至User类的扩展函数中
- <1> 将一段代码提取到一个扩展函数中的作用非常哇塞
- User 是代码库的一部分,而不是库中的一个类
- 你也不想把这个逻辑放到 User 的一个方法中
- 因为它与 User 在其他地方的使用无关
- 所以基于特殊情况应该使用扩展函数
- 类的API就只包含在所有地方都会用到的基本方法
- 这样类就会保持小巧,易于理解
- User 是代码库的一部分,而不是库中的一个类
- <1> 将一段代码提取到一个扩展函数中的作用非常哇塞
// <1>
fun User.validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user ${id}: empty $fieldName")
}
}
fun saveUser(user: User) {
user.validate(user.name, "Name")
user.validate(user.address, "Address")
// Save user to the database
}
============================================ 更进一步的扩展
// <1>
fun User.validateBeforeSave() {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user ${id}: empty $fieldName")
}
}
validate(name, "Name")
validate(address, "Address")
}
fun saveUser(user: User) {
user.validateBeforeSave()
// Save user to the database
}
- 扩展函数的使用总结如下
- 当在很多地方都会使用到某个功能时,建议将其声明为该类的成员方法
- 当某个功能只在局部访问并需要访问该类的其他成员函数时,建议声明为扩展函数
- 扩展函数也可以声明为本地函数,因此您可以更进一步
- 将 User.validateBeforeSave 作为 saveUser 中的一个本地函数
- 但是,深度嵌套的局部函数通常可读性很差
- 所以一般来说,我们不建议使用多于一层的嵌套
- 当在很多地方都会使用到某个功能时,建议将其声明为该类的成员方法