Java 的 idiom(7)

避免 naming pattern 使用 annotation

Java 的 annotation 当然不仅仅只是在解决 naming pattern 上有用,annotation 本质上是为 类、方法、成员增加额外的属性,这些属性可以被各种 framework 使用,比如测试的知道这个是个 test case,数据库知道这对应于什么 table、column,web service 知道这是一个 controller 对应的 URL mapping 等等。(参看原先一篇关于 Java annotation 的 blog

所谓的 naming pattern 就是希望通过某种命名习惯来表示某种作用,比如以 test 开头的方法都是 test case,naming pattern 只能携带非常少的信息,而通过 annotation,我们可以为这些元素添加各种复杂的东西。

一致的使用 @Override

现在不管是实现 interface 还是覆盖原先的实现都可以用 @Override。避免错误的函数名导致没有看到需要的 polymorphism。

使用 marker interface 与 annotations

所谓 marker interface 指一个 interface 并不定义任何 public 函数,比如 java.io.Serializable。与 annotation 相比这两种策略各有好处,marker interface 定义了一个新的类型,而 annotation 不行。但是 annotation 的好处是一旦有一个大的 framework 选择了 annotation,很多方面可以单独被一些 annotation 进行 mark,通过 annotation 可以加入各种需要的信息,并且能定位到成员、方法、函数参数等等。

选择这个问题有时候很难说,但是 annotation 要灵活一些,marker interface 需要做的可以通过若干个 annotation 也能做出来,但是就会显得麻烦,并且需要拖到运行时才能发现错误。

函数/方法的一些习惯

参数检查往往是这些的第一步,一般说来或者自己检查,不正确的时候抛出异常或者返回,或者要求 caller 检查,这时需要在文档里面注明要求,并通过 assert 断言,这个的作用和 C/C++ 的 assert 宏类似,通过参数 -ea 可以打开这个检查,否则就会被扔掉。

如果正在为一个 immutable 类提供构造函数,需要注意其成员是否有 mutable 的,否则就算这个类没提供相应的修改状态的函数,但由于传递引用,caller 仍然有机会在构造了这个对象后,通过原先对这个 mutable 成员的引用进行修改,这种情况下需要进行 defensive copy,即对 mutable 成员进行 copy,保证这部分成员与外界断绝关系。通常这发生在参数检查以前。检查应该针对复制后的进行。同时返回成员的时候,mutable 只能返回 defensive copy 而不能返回直接的引用。哎,想念死了 const 了…

当然 defensive copy 是有高昂的代价的,也许可以在 package 内部使用一个简单的 case 而逼迫外部的 client 使用 defensive 的版本,但是感觉这个 solution 从头到尾都是很差的。

还有一些规则,如名字应该尽量符合规范,不要过分追求简化参数,也不要弄一个很长参数列表的(可以尝试分解成为几个方法,每个方法参数列表较短;通过 helper class,如 builder pattern 可以看成是 constructor 的 helper class;),尽量利用 interface 而不要使用实现的 class 作为参数(增加适用性),避免使用 boolean 而使用表意更明确的 enum。

谨慎对待函数重载,overloading 发生在 compile-time,需要 dynamic 行为的必须用 instanceof 进行区分。

对可变长度参数列表的函数应该注意,如果要求参数多于 0,那就应该在参数列表上加一个参数而不是事后判断长度(累赘的写法)。另外与早期以数组形式表示的可边长度参数列表相比,有些很微妙的问题,但是好坏是双向的。注意使用这种技术会导致额外的开销(创建一个数组),如果事后分析表示这个开销不能接受,可以特化一些通过函数重载的方式将需要的情况用特化的实现解决。

返回容器或者数组的时候避免在没有结果时返回 null,而应该返回 length/size = 0 的,这样就不必要做额外的 == null 判断,而可以直接通过遍历干活(为空的时候自然什么都不干)。更重要的是某些 final 变量可能需要其返回值,如果返回 null 以后是不能修改的了…

书写 Javadoc 的一些建议

public method 必须要写 doc。其实很多公司的 code 都没写… 现在可以用 @code 来写代码段,早期通过 html 的 tag 来做需要转意字符,现在这个不需要了,对应 inline 的有 @literal。@param <T> 可以为 generic type 写说明,记得为所有需要的 parameter 都提供。enum 需要对每个值进行说明。对 annotation 的注释记得所有的成员都应该说明。

  • 最小化变量的 scope:lazy 的策略,不到用时不定义!通常这也意味着尽量将不同的事情分开到不同的方法里面去做。
  • 尽量使用 for-each:对 iterable 都能用,不仅形式上比传统的 for 简洁,也更不容易出错。也有情况是不行的,比如 filtering、transform 和并行。
  • 使用 lib 但是首先要了解它们
  • 在需要精确值时避免 float/double,其实这是个偏见,准确地说 float 和 double 应该考虑误差意义下的相等
  • 使用 primitive 而尽量避免 boxed primitive,这是效率考虑
  • 如果有替代品,避免使用 String 表示的区分,使用 enum 之类的
  • 了解 String 拼接的代价,这主要是因为 String 是 immutable 的
  • 尽量使用 interface 引用指代对象
  • 尽量使用 RTTI 而避免使用 reflection,只有专有的 lib 一般才涉足 reflection 的一些用途
  • 谨慎使用 native implementation(似乎 Java 对自己的性能还比较有自信,别的动态语言都是把一些 costly op 用 native code 实现的)
  • 谨慎进行优化,似乎这一点也是 java 与 C/C++ 不同的,原则是写好的程序而并不见得一定是效率最高的
  • 与 Java 广泛使用的 naming convention 保持一致
  • 仅在需要的异常的时候使用异常
  • 如果是可以挽救的话使用 checked exception,要求 client 解决;否则使用 unchecked exception(继承 RuntimeException)
  • 避免可能避免的 exception,比如通过提供检查的函数使得 exception 在某些情况下不会出现。
  • 尽量使用标准库提供的 Exception,主要有 IllegalArgument、IllegalState 表示参数问题或者状态不正确时被调用,另有 ConcurrentModification(单线程设计被多线程使用),UnsupportedOperation(进行了不支持的操作请求),NullPointer(不应该为 null),IndexOutOfBounds(下标越界)。
  • 根据 higher level 进行合理的 exception translation,即将一些 low level 的 exception 捕获后以 higher level 的 exception 重新抛出,这样出错信息便于理解
  • 为异常提供文档,如果是局部,应该在方法的文档里面 @throws 写清楚,在类声明的地方也可以写使用这个 exception 的期望位置什么的
  • 抛出异常时尽可能传递出错地方的信息
  • failure atomicity 即产生异常后对象应恢复到调用此方法前的状态
  • 不要为了捕获异常而捕获,捕获之后却不利用

——————-
And it came to pass the same day, that Isaac’s servants came, and told him concerning the well which they had digged, and said unto him, We have found water.

Advertisements
Java 的 idiom(7)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s