实践编程已经有足足6年多时间,也算是有一定经验,经常在工作中遇到各种让人不爽的代码编写方式,今天忍不住要来唠叨下。
为什么叫编程之美? 在我看来,代码有丑陋难看和赏心悦目两类,当然还有介于两者之间的。优秀开源框架的源码,让人看了就觉得舒服;而有些新手写的代码,让人看了别扭、添堵。本人也是从新手一步步过来的,想想自己早年写得代码,也是很丑的。当年丑不要紧,一直丑就要命了。
废话不多说,上干货。咱们就以java语言为例,对比说明”丑”跟”美”的代码(其实跟具体语言没有关系,仅以java语言举例)
一、全局静态常量满屏飞
此话怎讲,就是定义了很多静态常量,不适用的情况也在用。就举表设计中常有的status(状态)字段,一般有正常、删除、禁用这三种状态
丑的实现如下:
public static final int NORMAL = 0;//正常
public static final int DELETED = 1;//删除
public static final int FORBIDDEN = 2;//禁用
然后在代码各个地方引用这些静态常量,然后我就醉了,原因先不说,先说怎么改进
美的实现如下:
/**
* <数据状态枚举类>
*/
public enum StatusEnum {
NORMAL(0, "正常"), DELETED(1, "删除"), FORBIDDEN(2, "禁用");
private int code;
private String meaning;
StatusEnum(int code, String meaning) {
this.code = code;
this.meaning = meaning;
}
/**
* @return 返回 code
*/
public int getCode() {
return code;
}
/**
* @return 返回 meaning
*/
public String getMeaning() {
return meaning;
}
/**
* 可读的状态
*
* @param code
* @return
*/
public static String readableInfo(int code) {
StatusEnum ret = null;
for (StatusEnum e : StatusEnum.values()) {
if (e.code == code) {
ret = e;
break;
}
}
return ret == null ? "" : ret.meaning;
}
}
很显然,我们改用了枚举类来实现,因为状态就那么几种,是可以列举完的。实际中很多情况都是适用枚举类来实现,而不是静态常量,比如用户性别(只有男/女/保密三种可能)、用户类型(比如普通/会员制)、用户职业(有很多职业,但肯定是个有限确定的范围)等等。
那么这么改进的好处是什么呢?
简单说,这么改符合程序设计的开闭原则。所谓开闭原则,就是指对扩展开放、对修改关闭。
请注意StausEnum类的公有静态方法readableInfo,作用就是将status的int值转换成可读的字符串,以方便在web页面展示。假设将来新增了一个状态3,代表已审核,美的实现中只需要新加一个枚举常量
CHECKED(3, "已审核")
其它任何地方都不需要做修改,是不是很酷啊。
而如果是丑的实现,则需要在页面或action中修改类似判断(如果是jsp页面,判断的方式可能是<c:if>或<c:choose>方式)
String statusReadable = "";
if(status == NORMAL){
statusReadable = "正常";
}else if(status == DELETED){
statusReadable = "正常";
}else if(status == FORBIDDEN){
statusReadable = "正常";
}else if(status == CHECKED){
statusReadable = "已审核";
}
如果很多地方都写了类似的代码,就意味着你需要改N个地方,是不是想死的心都有了。
通过封装StatusEnum枚举类,将status的修改局限在这个类的内部,内部状态的增减或修改对外部是完全透明的,这就是开闭原则的一个典型例子。
下面说下修改这种情况,虽然不常见,但还是可能出现。
诡异情况举例
假如状态改为-1表示删除,对于丑的实现,只要修改下常量定义就可以,即改为
public static final int DELETED = -1;//删除
这没什么问题。但是在升级时,千万不要以为只升级这个类的class文件就行了,而是需要将引用到这个静态常量的所有相关类的class文件都进行升级,不然就会出现开发环境正常,到了线上就是不正常的诡异事情。
这个情况是怎么来的呢?
这就要说到java编译了,java编译就是将java源代码编译成java字节码文件,即class文件。只有编译成class文件,才能在JVM上运行,程序才能跑起来。
而在java编译时,会将类中引用的静态常量(该静态常量必须是基本数据类型或String常量)用他们的字面量代替,也就是编译后的class文件中,只存在字面量(比如int类型的字面量0,-1,1等),并不存在对该静态常量的引用。如果光升级定义静态常量的类的class文件,实际上引用到这个静态常量的其它class文件中的字面量并没有更新,就会造成升级失败。
而如果采用的是StatusEnum类的实现,就不会出现这个情况,修改为
DELETED(-1, "删除")
升级时,也只需要升级StatusEnum类对应的class文件即可。
总结
项目中很多情况适用枚举类,而不是静态常量方式。好好利用枚举实现,你会发现代码比以前简洁明了了,扩展和可维护性增强了,而且不容易犯错。
假如方法的接收参数类型是StatusEnum类型,你不可能传成状态范围之外的值
而如果接收的参数是int类型,则完全有可能传成0,1,2之外的值,这样就有可能产生隐藏BUG。
编程之美,未完待续,欢迎大家拍砖