方法的参数验证究竟应该是在调用者中验证还是在被调用者中验证,和异常处理一样(checked or unchecked),这又是个有争议的口水战。
举例:
Void firstMethod(List<String> param){
If(param == null)
Throw new IllegalArgumentException(“param can’t be null”);
secondMethod(param);
}
Void secondMethod(List<String> param){
If(param == null)
Throw new IllegalArgumentException(“param can’t be null”);
//do something others
}
在这个例子中,方法firstMethod调用了secondMethod,两个方法中同时对参数进行了null值校验。
乍一看,这是毫无疑问的重复校验,导致了代码冗余。但是在不同的场景下,情况会不太一样。例如这两个方法在不同的层(例如一个在Action层,一个在Service层),也可能是不同的程序员开发的。这下,问题就不太容易有个明确的答案了。有的人认为都应该校验,有的人认为只需要在某一端进行校验。所有观点大致分为两派:1.防御派。方法应该为自己负责,我不能保证调用者是否进行了校验,所以我必须要进行校验,从而保证程序的健壮性。2.简约派。方法应该校验,但是不应该重复校验。重复校验产生了冗余的代码,导致程序可读性差。
这两派掐来掐去,都想对方认同自己的观点,最后就变成了口水战。就像异常的口水战一样,关键是两种方式都能work,并没有一个标准答案。这个问题折中一下,就是:没有最好的,只有最适合的,适合自己的就是最好的。这话,很多人听起来都像是废话,我觉得这跟中国的教育有关,中国的各门功课都讲究一个标准答案,就连阅读理解这种仁者见仁智者见智的科目都有标准答案。所以我们习惯了有一个标准答案,最好就一个选择,我死记硬背就行了,千万不要有适应场景的选项,多费脑子。
OK,既然没有标准答案,那我们就先分析一下软件开发中都有哪些常见的场景,以及在这个问题上,适合用哪种方式。这里不管方法调用是否是来自同一个层或者来自同一个人。
- 开发自用方法(private or package scope),不需要校验。
(1) 对于private方法,应该保证传入的参数是正确的。因为它不会被不明真相的群众调用。由于完全被方法所在的类调用,所以方法的调用者应该保证传入参数是正确的。
(2) 对于包可见的方法,这里的包可见是指方法所在的类是package scope并且方法是private权限以上的。这种方法只能被同一个包内的其他方法调用,是可控的,所以应该保证传入的参数是正确的,不管是不是同一个人所写。这一点可以通过项目约定来规范。
Effective java的作者建议使用assert来校验(可以通过jvm参数来启用或者暂停assert)这类方法,这个看大家的需求了(可选)。
- 开发导出方法,必须校验。(public)
Public的方法是指方法及其所在的类都是public的,对于这种情况,如果包被导出,则这个方法会被任何人使用。这种情况下是无法保证调用者的行为的,所以我们的方法应该对自己负责,只要参数不正确,就应该有相应的处理,而不能任由错误的参数破坏了数据。
另外,从专业性的角度考虑(我们应该尽量让自己成为专业的开发人员,就像java 类库的开发人员),我们应该在方法的comment中(不管是private、package scope、protected还是public)注明参数应该满足的规则,以及不满足时,方法时如何处理的(return or throw exception)。还有,就是大家普遍忽略的问题,就是喜欢把类设计成public的,不管这个类需不需要导出,这样就破坏了封装。从本章的角度考虑,这些方法也需要进行校验,虽然实际上并不需要。