许多开发者认为,优秀代码最重要的一个质量是没有冗余。这个原则还有一个别名:不要重复(Don’t Repeat Yourself, DRY)。但是当你用Java写代码时,遵循这个原则并非总是无关紧要的。在许多场合,使用IDE的提取函数重构功能来讲冗长的函数分解为更小的代码块并重用它们是可能的。然而,这可能会导致代码变得难以理解,因为你可以走的更远一点并把提取出的方法分组到一个内部类中,这可以让你维护代码结构,但是这个方法会引入一定数量的八股代码。  Kotlin给你一个更加清晰的解决方案:你可以在容器函数中嵌套你已经提取的函数。通过这种方式,你保留了你需要的结构而无需额外的代码。 让我们来看看如何使用本地函数来解决相当常见的代码冗余问题。在下面的例子中,函数将用户信息保存到数据库中,你需要确保用户对象包含的是有效的数据:


class User(val id: Int, val name: String, val address: String)
fun saveUser(user: User) {
  if (user.name.isEmpty()) {
      throw IllegalArgumentException(
          "Cannot save user ${user.id}: Name is empty")
  }

  if (user.address.isEmpty()) {  // 1 字段验证是冗余的(代码)
      throw IllegalArgumentException(
          "Cannot save user ${user.id}: Address is empty")
  }

  // Save user to the database
}
>>> saveUser(User(1, "", ""))
java.lang.IllegalArgumentException: Cannot save user 1: Name is empty

这里的代码冗余比较小,你可能不想在你的类中到处都是一个处理用户验证的特殊场景的方法。但是如果你把验证代码放到一个本地函数中,你可以避免冗余,同时保持清晰的代码结构。以下是它的原理:


class User(val id: Int, val name: String, val address: String)
  fun saveUser(user: User) {

    fun validate(user: User, // 1 声明一个本地函数来验证任意的字段
                value: String,
                fieldName: String) {
      if (value.isEmpty()) {
        throw IllegalArgumentException(
          "Cannot save user ${user.id}: $fieldName is empty")
    }
  }

  validate(user, user.name, "Name") // 2 调用本地函数来验证特定的字段
  validate(user, user.address, "Address")

  // Save user to the database
}

这样的代码看起来更加的好。验证逻辑没有重复,随着项目的发展,如果你需要添加其他字段到User对象,你可以方便的添加更多的验证代码。但是必须将User对象传递给验证函数是有点丑陋的。好消息是,这种做法是完全不必要的,因为本地函数已经访问了嵌套函数的所有参数和变量。让我们利用这个优势来避免额外的User参数:


class User(val id: Int, val name: String, val address: String)

  fun saveUser(user: User) {

    fun validate(value: String, fieldName: String) { // 1 现在你没有重复saveUser函数的“user”参数

      if (value.isEmpty()) {
        throw IllegalArgumentException(
          "Can't save user ${user.id}: " + // 2 你可以直接访问外部函数的参数
          "$fieldName is empty")
    }
  }

  validate(user.name, "Name")
  validate(user.address, "Address")

  // Save user to the database
}

>>> saveUser(User(1, "", ""))
java.lang.IllegalArgumentException: Cannot save user 1: Name is empty

为了进一步提升这个例子,你可以把验证逻辑移动到User类的扩展函数中:


class User(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave() {
  fun validate(value: String, fieldName: String) {
    if (value.isEmpty()) {
      throw IllegalArgumentException(
        "Can't save user $id: empty $fieldName") // 1 你可以直接访问User对象的属性
    }
  }

  validate(name, "Name")
  validate(address, "Address")
}

fun saveUser(user: User) {

  user.validateBeforeSave()  // 2 调用扩展函数

  // Save user to the database
}

抽取一部分代码到一个扩展函数中,结果竟是如此的有用。尽管User是你的代码的一部分而不是一个库中的类,由于这跟任何其他使用User的地方不相关,你不想把这个逻辑放到User的方法中。因此,类的API仅仅包含了每个地方都需要用到的方法。所以类依然保持紧凑,同时很便于围绕你的想法(来展开)。另一方面,如前一个例子所示,函数主要处理一个单独的对象并不需要访问它的私有数据,可以访问它的成员却没有额外的限制。  扩展函数也可以声明为本地函数,所以你可以进一步把User.validateBeforeSave()放在saveUser()中作为本地函数。但是多重嵌套的本地函数往往是难以阅读的。因此,原则上,我们不推荐使用超过一个层级以上的嵌套。  看完函数中比较酷的特性,在下一章里,我们来看看你可以用类来做些什么。

results matching ""

    No results matching ""