我坚信如果写不好一个方法,是不可能写好一个类,写不好一个类,就谈不上好的设计,设计一个方法签名及实现,其实有很多学问,我们知道一个方法有返回值,方法名,方法体,方法参数。 因此,设计并写好一个方法要考虑这些要素要如何处理。当然设计方法不能脱离类,但假设已经在类职责范围内定义好了一个方法签名。
在代码整洁之道一书,有一章专门用来描述方法设计,其中有一点是说:
1. 当设计函数的界面时,要选择使程序员第一次就能够写出正确代码的设计。不要使用引起混淆的双重意义的返回值,其中针对这个问题举了一个getchar()示例。
从那起,深深意识到,你的API就像是一种契约合同,在书写时,你必须先明确你的方法目标,并有注释说明,你不能模糊处理,擅自更改处理。
在实现方法体时,有哪些注意事项呢?
1. 如果我的方法是明确给外部使用的,我们可以规定什么样的参数是合法的,如果传递非法参数,我们必须告知对方,通常也就是通过抛送异常来处理
2. 当我的方法参数都是合法时,但是因为某种原因无法进行下去,这个时候,你应该抛送异常,明确告知无法进行下去的原因,而不是擅自返回一个随便的值,这会掩盖错误以及歪曲目标真实意图功能。
3. 当我的方法顺利执行完毕,我们可以把执行结果返回告知调用方。
事实是,我们可能有时会歪曲方法真实意图
看看如下方法:(当num为null或者为空串时,返回0值,该方法的目标意图真的是希望这样吗?)
long toLong(String num){
if(StringUtils.isBlank(num)){//当num为null或者为空串时
return 0L;
}
return Long.parseLong(num);
}
而且上面还存在一个问题,如果num内部不符合数值转换格式,是会抛出异常
总结如下:
1. 当num为null或者为空串时,内部自我假设返回0值;
2. 如果num不为null或者不为空串时,但num内部不符合数值转换格式,是有可能抛出异常
这就造成当num无法转换数值时,有两种不同的表现结果。可见有多糟糕!
有几种方案可以用来改进:
第一种:明确API的目标意图就是在无法转换数值时,直接返回一个默认值。(这种API适合有默认约定的场景调用,常见的如配置,当无法获取配置,或没有配置时,我们可以约定默认配置)
long toLong(final String num, long defaultValue){
if(StringUtils.isBlank(num)){//当num为null或者为空串时
return defaultValue;//内部自我假设返回0值,这是一种明显的错误,这个0代表的是一种错误,还是真的就是0值?
}
try{
return Long.parseLong(num);
}catch(NumberFormatException e){
return defaultValue;
}
}
第二种:明确API的目标功能就是要返回转换后的数值,当无法转换时,必须中断处理,有两种处理方式,抛送异常或者返回错误信息
抛送异常:
long toLong(final String num) throws IllegalArgumentException, NumberFormatException{
if(StringUtils.isBlank(num)){//当num为null或者为空串时
throw new IllegalArgumentException("num is null or blank ");
}
return Long.parseLong(num);
}
返回错误信息:
Result<Long> toLong(final String num){
if(StringUtils.isBlank(num)){//当num为null或者为空串时
return new Result<Long>(Code.ParameterIsNotValid, null);
}
try{
return new Result<Long>(Code.Success, Long.parseLong(num);
}catch(NumberFormatException e){
return new Result<Long>(Code.ParameterIsNotValid, null);
}
}