[旧文搬迁]走出“最小公开化”的误区


感谢IT牛人博客聚合上还能找到我之前博客的一些内容,因为之前主机没续费等各种原因,也懒得翻之前的备份文档了,就把那上面的文章直接拷过来整理一下好了,只搬迁一些还有意义的内容好了,其他的就让它随风而去吧。

记得刚接触面向对象的程序设计时,就知道了这样一个原则,就是类的成员最小公开化。由于当时做的基本是特定开发,再加上各种教程的“误导”,所以想当然的认为能private的绝不friendly,能friendly的绝不protected。当然,其实protected都很少会用,大部分时候都是public或者是private的。然而最近在做写终搜新版本以及看一些经典开源代码时,发现其实很多时候都是protected或者friendly的,并不是清一色的private,这不得不让我思考,到底什么是最小公开化原则。

首先先来复习一下Java中四种可见度,首先private是私有的,只有这个类的成员可以访问,可见度最小;可见度第二小的是friendly,只对同一包内的类及其成员可见;第三小的是protected,对该类的派生类及同一包内的类可见;public顾名思义。之所以会有可见度,是因为要避免这些方法或者成员在其作用域外被误用,导致程序出现不可预知的行为。

按照大多数POJO的例子,其域成员都是private的,然后通过getter和setter去读写其值。所以以前写的绝大多数类都是私有成员,但放在产品的角度来考虑,这样真的合适么?当你觉得某一个非final类的行为并不符合你的需求时,你的第一想法是什么?继承这个类,然后用派生类代替该类,重写不符合预期的想法。现在问题来了,既然很多成员都是私有的,那么在你的派生类中就不能直接利用这些成员了,要么自己定义成员,要么用臃肿的setter和getter吧。为么会有继承?很大程度上是为了实现代码的高度复用,但是现在你的代码的使用者不得不继承你的类,然后重写这些需要用到的成员,并一边骂着:“靠,用个protected会死啊!”

没错,最小公开化不代表完全私有化,在确保其不在作用域外被误用的情况下,还要尽可能的“开放”以为你的后人提供方便。那么到底什么情况下适合用private呢?如果你确信这个类永远也不可能被继承重写,那么你大可以给类加上个final,然后放心大胆的用private去标注那些需要隐藏的实现。另外一种情况,如果你的属性或者方法只是某个方法特殊的中间调用产物,而你有确信这段代码不会被其他人复用,那么你可以放心的用private。如果你觉得你的类可能会被继承重写,那么最后给每一个要隐藏的成员标注protected,这样既为你的代码的使用者提供方便,又起到了保护代码的作用。

但问题并不是这么简单,问题就在于Java中protected的语义,按照我们传统观念来说——也就是目前C#, Scala等语言中的protected那样,protected修饰的成员应该只对该类及其子类的成员可见,但很遗憾,不知道Java最初的设计者出于哪方面考虑,protected也可以被同包的类访问,也许是因为最初的类库一个包中的类经常共用一些成员,但是又与其他包的业务无关吧。总之,这是游戏规则的一个失误。你也许会说,大量使用protected也许会不小心被同一个包的类误用,但是,必须说的是,这个做法本身是一个错误的用例。而且,别忘了,Java提供了反射,这个足以改变游戏规则的武器,所以没有什么代码是真正安全的,除非你自己足够小心。

所以,尽量多用protected吧,当你有一天需要继承先前类,并且只重写一两个非公开方法时,你会感觉到非常爽的。


文章作者: Odin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Odin !
  目录