【解密 Kotlin 扩展函数】扩展函数让代码更整洁 (二十二)

1.1 本地函数和扩展函数

  1. 许多开发人员认为,好代码最重要的品质之一就是不重复

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值