作为程序员,我们总是和方法打交道,不知不觉都会接触Long method(方法体较长的方法),不论是自己写的还是他人写的,而Long method(长方法)往往是问题的体现,代表着代码有一种坏的味道,也意味着需要对这段代码进行重构处理。
长方法的问题通常表现在
既然长方法不好,那么我们就应该写short method(短方法),但是什么样的方法才算短方法呢,有什么衡量呢?
首先我们想到的可能是限制方法的行数,是的,有人说是20行为宜,有人说是10行最佳,众说纷纭,无一定论。
但是行数限定也有问题
显然除了行数之外,我们需要一个更加明确无争议的避免长方法产生的方法,比如今天我们提到的 SLAP(单一抽象层原则)。
SLAP 是 Single Level of Abstraction 的缩写。
关于SLAP的一些具体解释
指定代码块的代码应该在单一的抽象层上。
其实关于定义最难理解的应该是抽象层,其原因可能在于
举一个最简单的例子,在中学时期我们学习英语,大概听过一个这样类似的短句”美小圆旧黄法国木书房”,这是为了辅助在英语中快速排列定语顺序的记忆技巧总结。
在英语(或其他语言)中
比如我们对“美小圆旧黄法国木书房” 逐步删除定语,大致会产生这样的抽象层
我们回归编码,来看一个例子
private boolean validateUser(User user) {
//检测邮箱是否合法
String ePattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-][email protected]((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(ePattern);
java.util.regex.Matcher m = p.matcher(user.email);
if (!m.matches()) {
return false;
}
//检测密码是否合法
if (user.password.length() < 8) {
return false;
} else {
for (char c : user.password.toCharArray()) {
if (!Character.isLetterOrDigit(c)) {
return false;
}
}
}
//return true if it goes here.
return true;
}
上面的代码
上面代码存在的问题是
解决方法
所以按照SLAP原则修改之后的代码应该类似于
public class UserValidator {
public static final String EMAIL_REGULAR_EXPRESSION = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-][email protected]((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
public static boolean validateEmail(String email) {
Pattern p = Pattern.compile(EMAIL_REGULAR_EXPRESSION);
return p.matcher(email).matches();
}
public static boolean validatePassword(String password) {
if (password.length() < 8) {
return false;
} else {
for (char c : password.toCharArray()) {
if (!Character.isLetterOrDigit(c)) {
return false;
}
}
}
return true;
}
}
private boolean validateUserSLAP(User user) {
return UserValidator.validateEmail(user.email) && UserValidator.validatePassword(user.password);
}
//注释1
代码片段1
//注释2
代码片段2
//注释3
//代码片段3
上面的代码
public List<ResultDto> buildResult(Set<ResultEntity> resultSet) {
List<ResultDto> result = new ArrayList<>();
for (ResultEntity entity : resultSet) {
ResultDto dto = new ResultDto();
dto.setShoeSize(entity.getShoeSize());
dto.setNumberOfEarthWorms(entity.getNumberOfEarthWorms());
dto.setAge(computeAge(entity.getBirthday()));
result.add(dto);
}
return result;
}
上面for循环体内部的代码,处理了将ResultEntity转化成ResultDto,可以完全单独抽离成单独的方法,如下代码所示
public List<ResultDto> buildResult(Set<ResultEntity> resultSet) {
List<ResultDto> result = new ArrayList<>();
for (ResultEntity entity : resultSet) {
result.add(toDto(entity));
}
return result;
}
private ResultDto toDto(ResultEntity entity) {
ResultDto dto = new ResultDto();
dto.setShoeSize(entity.getShoeSize());
dto.setNumberOfEarthWorms(entity.getNumberOfEarthWorms());
dto.setAge(computeAge(entity.getBirthday()));
return dto;
}
除此之外,回调方法也是容易形成长方法的重灾区,这一点无需再多举例。
首先,必须承认,SLAP应用后,会产生一些短方法,但是关于维护成本提升,这一点还是需要考究的。
因为
所以,不要畏惧,短方法的产生,应该是喜欢上短方法。
SLAP是Single Level of Abstraction的缩写,不是Same Level of Abstraction,?