一般而言,自然界是以对称为美的。但是,在编程的世界里面,不对称的情况比比皆是,一样的美。
常见的api中有如下例子:
// String的方法。
public String substring(int beginIndex, int endIndex)
// List的方法。
List<E> subList(int fromIndex, int toIndex)
这里一直遵循着不对称的左闭右开原则。
这种原则有2个显著的优点。
1 计算该范围所包含的数量时,直接endIndex-beginIndex就好了。如果是左闭右闭区间的话,则需要形如endIndex-beginIndex+1的方式来计算。
2 左闭右开区间有良好的叠加性。举个例子,一个[0,100)的范围,自然等于[ 0,10), [10,20),…,[90,100)的叠加。
了解了这种不对称的优点,那么我们设计接口的时候,应该尽量遵循左闭右开原则。
/**
* 查找在一定时间范围内加入系统的用户。包含start时间点,不包含end时间点。
* */
public List<User> findUser(Date start, Date end)
再看一个例子:
public static final String FILE_SEPARATOR = File.separator;
private String baseDir;
public String getFileDir(String userId, String date) {
String relativePath = getDateDir(date) + getUserDir(userId);
return getBaseDir() + FILE_SEPARATOR + relativePath;
}
private String getUserDir(String userId) {
return FILE_SEPARATOR + userId + FILE_SEPARATOR;
}
private String getDateDir(String date) {
return date + FILE_SEPARATOR + date;
}
private String getBaseDir() {
return baseDir;
}
这段代码的功能是构建一个用户在特定日期的文件路径目录。该文件路径目录由三部分组成,baseDir是配置的根目录,日期目录是根据日期所得到的目录,用户目录是根据用户id得到的目录。
在写这段代码的过程中,很明显没有考虑separator和目录的关系,导致各个子目录风格不统一,每次使用类的方法时都要仔细的检查该目录是不是以separator结尾,以防出错。
很明显,使用不对称的原则可以很好的解决该问题,可以规定所有子目录都以separator开头,但是不以separator结尾。
重构后代码如下:
public static final String FILE_SEPARATOR = File.separator;
private String baseDir;
public String getFileDir(String userId, String date) {
return getBaseDir() + getDateDir(date) + getUserDir(userId);
}
private String getUserDir(String userId) {
return FILE_SEPARATOR + userId;
}
private String getDateDir(String date) {
return FILE_SEPARATOR + date + FILE_SEPARATOR + date;
}
private String getBaseDir() {
return baseDir;
}
当然,这里getFileDir没有了separator结尾,需要修改使用到该方法的地方。
做了该重构以后,所有子目录的格式变为统一的,方便记忆和使用,并且极大的简化了目录拼接问题,由于separator不对称的使用,现在目录的拼接只需要简单的使用子目录拼接即可,不用关心separator的问题了。