在开发过程中,给函数起个好名字十分必要,好的名字可以缩短维护时间。
变量、函数或类的名称可以表现出程序大部分功能含义,真正好的命名不需要注释就应该知道其含义。那么, 如何写出优雅的代码?
一、见名知意
举个栗子🌰1:
1 int m //月
如果不看注释,应该不会知道m究竟表示何意,是月份(month)、米数(meter)还是分钟(minute),在变量定义过程中,尽可能要见名知意。
举个例子🌰2:
1 public List<int []> getList(){
2 List<int []> list1=new ArrayList<int []>();
3 for(int [] a:theList){
4 if(a[0]==4){
5 list1.add(a);
6 }
7 }
8 return list1;
9}
上面代码的格式没有问题,问题出现在代码表达上,命名太过模糊,很难知道其表达是何含义。
(1)theList中是什么类型?
(2)theList零下标条目的意义是什么?
(3)值4的含义是什么?如果我们开发的一款扫雷游戏,theList代表的是盘面列表,那么theList就应该改为gameBoard。盘面上每一个单元格都表示一个简单数组表示,零下标其实表示的是一种状态值,等于4表示为“已标记”。
只要改成有意义的名称代码就会得到改进:
1 public List<int[]> getFlagedCells(){
2 List<int []> flaggedCells=new ArrayList<int []>();
3 for(int [] cell:gameBoard){
4 if(cell[STATUS_VALUES]==FLAGGED){
5 flaggedCells.add(cell)
6 }
7 return flaggedCells;
8}
代码格式完全没变,只是变换了名称,表达意义就完全不同。还可以进一步改进,不用int数组表示单元格,而是写一个类,用一个类去包装int数组,从而掩盖掉魔术数,于是改进后如下:
1 public class Cell {
2 private static final Integer FLAGGED=4;
3 private int index;
4
5 public void setIndex(int index) {
6 this.index = index;
7 }
8 public booleanis isFlagged(){
9 if(this.index==FLAGGED){
10 return true
11 }
12 return false;
13 }
14}
1 public List<Cell> getFlagedCells() {
2 List<Cell> flaggedCells = new ArrayList<Cell>();
3 for (Cell cell : gameBoard) {
4 if (cell.isFlagged()) {
5 flaggedCells.add(cell);
6 }
7 return flaggedCells;
8 }
9 }
10}
不用int表示单元格,而是写一个类,该类写一个状态函数(isFlagged)从而掩盖掉魔术数,让看代码的人,更能看懂其中含义,这就是选好变量名称带来的力量。另一方面,在实体属性的判断可以放在实体层,以往实体层只有set()、get()方法,这样的模型我们称之为“失血模型”,本质没有逻辑意义。
二、做有意义区分
函数和变量参数名尽量避免写无意义的废话,比如:
以数字命名(a1,a2,a3,…..,3N)没有任何意义,后期代码维护困难较大。
举个栗子🌰3:
1 public static void copyChars(char a1[],char a2[]){
2 for(int i=0;i<a1.length;i++){
3 a1[i]=a2[i];
4 }
5 }
上面函数是一个数组拷贝函数,如果把变量参数名改为source和destination会更好一些。《代码整洁之道》中有一句话:废话是没有意义的区分。假如代码中有很多种关于描述汽车信息名称,Car,CarInfo,CarData,它们的名称虽然不同,但是毫无区分意义,改成XXXCar会更好。Variable一词不应该出现在变量中,就好像table一词不应该出现在表中一样,NameString会比Name好吗?PhotoNumberInteger会比PhotoNumber表达的含义更清楚吗?
三、名称要易读
如果函数的名称不易读,那么交流起来也会变得十分困难。
举个栗子🌰4:
1 class MyProduct{
2 priavte Date genymdhms;
3 priavte Date modmdhms;
4 priavte final String paszqint ="102";
5 /*....*/
6}
改进后:
1 class MyProduct{
2 priavte Date generationTimestamp;
3 priavte Date modificationTimestamp;
4 priavte final String recordId ="102";
5 /*....*/
6}
四、使用可搜索的名称
使用可搜索的名称,不但可以很好表达属性的意义掩盖掉魔术数,也可以定位代码关键出处,维护起来更为方便。
比如 int 1 表示一个月第一天,但是代码中可能会出现几十个1,搜索起来极为不便,改成public static final Integer FIRST_DAY=1,则会简单的多。
五、不添加没用的语境
类名如果本身能够真实表达语境,那么就无需添加额外的语境。对于Address类来说,是一个非常好的类名,URL也是关于表示地址的非常的好的类名,无需使用URLAddress去添加额外address语境。只要名称能够表达清楚含义,就没必要添加额外的语境。但对于实体属性来说,accountAddress和customerAddress来说都是一个很好的命名。
六、类名和方法名
类名应该是名词或名词短语,如Customer、Wikipage、Account和AddressParse避免使用Date或者Info这样的类名,类名不应该是动词。
方法名应该是动词或动词短语开头,如postPayment,deletePage或者save、get。如果你是应用开发者或者维护者,不妨试试以上规则,优雅的代码应先从好的命名开始。以上就是本期分享,后续胖虎会继续给出如何写出优雅代码的系列分享。
我是胖虎,用心写好每篇文,下期再见。
参考书籍:[1]《代码整洁之道》[美]Robert C. Martin.人民邮电出版社.2010.