第30条:最小化元素的可见性
当我们设计一个 api 时,有很多原因可以解释为什么我们希望它尽可能的精简,让我们列出最重要的原因:
更小的接口更容易学习和维护,比起那些能做十几件事的接口,只做少数几件事的接口更容易让我们去理解,当我们做出改变时,我们通常需要理解所有实现类,当可见的元素的越少时,需要维护和测试的成本就越少。
当我们想要进行更改时,公开一个新元素要比隐藏现有元素容易的多。所有公共可见的元素都是我们的公共 API 的一部分,它可以在外部使用,一个元素的可见时间越长,它的外部用途就越多。因此,更改这些元素就更加困难,因为它们将需要更新所有的用法。限制可见性将是一个更大的挑战。如果是这样,就需要知悉每种业务用法并提供替代方案。 提供一个替代方案并不轻松,特别是如果它是由另外一个开发人员实现的。了解现在业务的需求可能也很困难,如果是公共库,限制某些元素的可见性可能会让一些用户感到气愤。他们将需要调整他们的实现,还将面临这样的问题 —— 他们将需要在代码开发多年之后实现一个替代的解决方案。最好一开始就强制开发人员使用较小的API。
当表示这种状态的属性可以从外部更改时,类就无法控制自己的这个状态了。我们假设一个状态可以满足一个类的需要,当这个状态可以从外部更改时,这个类就不能保证它的行为了,因为它可能被不知道内部约定的开发者从外部更改了。看一下第二章的 ConterSet
,我们正确地限制了 elementsAdded setter
的可见性,如果没有限制,有人可能会把它从外部更改为任何值,我们就无法相信这个值能否代表这个容器有多少个元素。注意 , 只有 setter
是私有的,这是一个非常有用的技巧:
对于许多情况,默认情况下在 Kotlin 中封装所有属性是非常有用的,因为我们总是可以限制具体访问器 (getter / setter)的可见性:
当我们有相互依赖的属性时,保护内部对象状态尤其重要,例如,下面 mutableLazy
的委托实现,我们期望如果 initialized
是 true,那么 value 就已经被初始化,类型是 T
。无论我们做什么 initialized
的 setter 都不应该暴露,否则它不能被信任,否则会导致属性在 get 时会出现一个丑陋的异常。
当类的可见性受到限制时,跟踪类的变化是很容易的,这使得属性状态更容易理解。当我们处理并发性时,这一点尤其重要。状态变化是并行编程的一个问题,最好尽可能地控制和限制它。
使用可见性修饰符
为了实现一个表面上很小,而内部可能很复杂的接口,我们限制了元素的可见性。一般来说,如果没有理由让一个元素可见,我们宁愿将其隐藏。这就是为什么当如果没有很好的理由限制较少的可见性类型,那么一个好的实践就是让类和元素的可见性尽可能小。我们使用可见性修饰符来做到这一点。
对于类成员,我们可以使用以下4个可见性修饰符:
public(default)
—— 对于看到能声明该类的客户端,被修饰的属性任何位置都可见,private
—— 仅在类内部可见protected
—— 仅在类内部和其子类中可见internal
—— 对于看到能声明该类的客户端,在模块内部任意位置可见
顶层元素有3个可见性修饰符:
public(default)
—— 任何地方可见private
—— 只在同个文件中可见internal
—— 只在同个模块中可见
请注意,模块与包不同,在 Kotlin 中,它被定义为一组一起编译的 Kotlin 资源,这意味着它可以是:
一个 Gradle 源码集
一个 Maven 项目
一个 Intellij IDEA 模块
用一次 Ant 任务编译的一组文件
如果你的模块可能被其他模块使用,请更改你不想公开的元素的可见性,如果一个元素是为继承而设计的,并且只在一个类和子类中使用,那么将其设置为 protected
。如果只在同一个文件或类中使用元素,则将其设置为 private
。Kotlin 支持这种约定,因为它会在 IDE 中提出建议:如果一个元素只在本文件使用,则需要将可见性限制为 private
此规则不该被应用于保存数据的类(数据模型类)中的属性,如果你的服务器返回一个带有年龄的用户,并且你要解析它,你就不需要将这个这些元素隐藏起来,它在那里的意义就是被使用,所以最好让它可见,如果你不需要它,那你得完全摆脱这个属性:
一个很大的限制是,当我们继承一个 API 时,我们不能通过覆盖它来限制成员的可见性,这是因为子类总是可以用作它的超类。这只是我们倾向选择组合而不是继承的另一个原因(第36条:优先使用组合而不是继承)。
总结
经验法则是:元素的可见性应该尽可能被限制。可见元素构成了公共 API,我们希望它尽可能的精简,因为:
更小的接口更容易学习和维护
当我们想要做出改变时,暴露比隐藏要容易的多
当代表这个状态的属性可以从外部被改变时,一个类就不能对它自己的状态负责了
当 API 的可见性受限制时,更容易跟踪 API 的变化
Last updated