在《重构 改善既有代码的设计》一书中,称一些不完美的,甚至写的很烂的程序叫做有“坏味道”。当程序有“坏味道”时我们就要对它进行重构。作为程序猿,做几次重构后你就会发现你喜欢上重构,不知道别人是不是,反正我是喜欢上重构了。重构是一种对逻辑的审查与修改的过程,在一次做完对一个系统40%代码的重构后,我把代码量减少了90%,但是代码变得更容易理解了,而且可扩展性更强了,那时觉得非常有成就感。脑子还瞬间蹦出来一个词——逻辑之美,但是感觉这应该是一本书的名字,但是网上搜了一下竟然没有这本书《逻辑之美》。

下面,我们来看一个我刚刚重构过的方法(部分注释是我为了读者容易理解加上去的,单词Indicator意思是指标的意思)。

 /* 功能:下面方法功能是对js框架dhtmlx的表格控件dhtmlxgrid对象一次刷新
  * 背景:本方法的应用背景是对mygrid内多行进行拖动排序,排序原理是先获取行的初始位置,目标位置,然后获取以数组形式获取mygrid中的数据(行id,每一个单元格的数据),将数组进行排序后重新填充进原表格mygrid
  * 参数:参数array是将要重新填充进表格的数组数据,参数mygrid的是将要操作的dhtmlxgrid对象
   */
function reCreatMygrid(array,mygrid){   //刷新新的指标列表
    mygrid.clearAll(false);   //不清除表头
    var cellIndicatorArrayLength = mygrid.getColumnsNum()+1;   //单位指标的数组长度(所有列的值加上行id)
   var newArray=[];  //用来存储指标列表内容
    var newArrayId=[];  //用来存储新列表的Id
   /*
   * 因为是提供给所有已定义dhtmlxgrid表格应用接口,所以每个表格的列数有差异
   */
   if(cellIndicatorArrayLength==7){    //如果表格总共有6列
           for( var i=0;i<array.length;i=i+7){
                 newArray.push([array[i],array[i+1],array[i+2],array[i+3],array[i+4],array[i+5]]);
                 newArrayId.push(array[i+6]);      
         }
   }else if(cellIndicatorArrayLength==6){    //如果表格总共有5列
           for( var i=0;i<array.length;i=i+6){
                 newArray.push([array[i],array[i+1],array[i+2],array[i+3],array[i+4]]);
                 newArrayId.push(array[i+5]);      
         }
   }else if(cellIndicatorArrayLength==5){     //如果表格总共有4列
           for( var i=0;i<array.length;i=i+5){
                 newArray.push([array[i],array[i+1],array[i+2],array[i+3]]);
                 newArrayId.push(array[i+4]); 
         }
   }else if(cellIndicatorArrayLength==4){
          for( var i=0;i<array.length;i=i+4){
                 newArray.push([array[i],array[i+1],array[i+2]]);
                 newArrayId.push(array[i+3]);      
         }
   }else if(cellIndicatorArrayLength==3){
          for( var i=0;i<array.length;i=i+3){
                 newArray.push([array[i],array[i+1]]);
                 newArrayId.push(array[i+2]);      
         }
   }
   //将数据填充进表格(dhtmlxgrid提供的接口)
   mygrid.parse(newArray,"jsarray" );
   /*
   * dhtmlxgrid提供的数组填充方式不能为其赋予行id,初始化后为默认id,即1,2,3……
   * 现在我们必须把每行原有的id赋给每一行
   */
   for( var j=0;j<mygrid.getRowsNum();j++){
         mygrid.setRowId(j, newArrayId[j]);
   }
 }

上面方法这是我一年前写的。这不,前两天这块出问题了,当mygrid的行过多的时候(其实也不多就60多行),当使用拖拽排序的时候,就会出现问题。什么问题呢?首先,拖动之后浏览器会出现崩溃现象;其次,经过我调试,发现问题就在最后一个循环里面,即dhtmlxgrid的原生的为行设置行id的方法setRowId会失效,就这么一个循环,但是设置了行id后,并不是理想状态的。我不想去查setRowId的原因了,因为我觉得我的这个方法本身就很烂,我想重构它。

对,就是重构。

重构的首要任务是解决bug,我得使用另一种方法初始化mygrid,即不用Array数据了,我打算使用Json格式 (dhtmlx控件基本都支持HTML table, xml,array,json),因为json格式可以再填充表格数据的时候同时赋给每行id值,它绝对可以解决目前的bug。经过查阅了dhtmlxgrid的帮助文档,我成功的完成任务,代码如下:

function reCreatMyrightgrid(array,mygrid){   //刷新新的指标列表
   mygrid.clearAll(false);   //不清除表头
   var cellIndicatorArrayLength = mygrid.getColumnsNum()+1;   //单位指标的数组长度(所有列的值加上行id)
   //var newArray=[];  //用来存储指标列表内容
   //var newArrayId=[];  //用来存储新列表的Id
   var rowsArray = [];   //存放行数据的数组[{"id":行id,"data":本行每一列的数据数组}]
   var dataAll = {};
   if(cellIndicatorArrayLength==7){    //如果表格总共有5列
         for( var i=0;i<array.length;i=i+7){
                //newArray.push([array[i],array[i+1],array[i+2],array[i+3],array[i+4],array[i+5]]);
                //newArrayId.push(array[i+6]);
            rowsArray.push({"id":array[i+6], "data":[array[i],array[i+1],array[i+2],array[i+3],array[i+4],array[i+5]]});
      }
   }else if(cellIndicatorArrayLength==6){    //如果表格总共有5列
         for( var i=0;i<array.length;i=i+6){
            //newArray.push([array[i],array[i+1],array[i+2],array[i+3],array[i+4]]);
            //newArrayId.push(array[i+5]);  
            rowsArray.push({"id":array[i+5], "data":[array[i],array[i+1],array[i+2],array[i+3],array[i+4]]});
      }
   }else if(cellIndicatorArrayLength==5){     //如果表格总共有4列
         for( var i=0;i<array.length;i=i+5){
            //newArray.push([array[i],array[i+1],array[i+2],array[i+3]]);
            //newArrayId.push(array[i+4]);
        rowsArray.push({"id":array[i+4], "data":[array[i],array[i+1],array[i+2],array[i+3]]});
      }
   }else if(cellIndicatorArrayLength==4){
         for( var i=0;i<array.length;i=i+4){
            //newArray.push([array[i],array[i+1],array[i+2]]);
            //newArrayId.push(array[i+3]);
            rowsArray.push({"id":array[i+3], "data":[array[i],array[i+1],array[i+2]]});
      }
   }else if(cellIndicatorArrayLength==3){
         for( var i=0;i<array.length;i=i+3){
            //newArray.push([array[i],array[i+1]]);
            //newArrayId.push(array[i+2]);
            rowsArray.push({"id":array[i+2], "data":[array[i],array[i+1]]});
      }
   }
   dataAll.rows=rowsArray;
   mygrid.parse(dataAll,"json" );
   //mygrid.parse(newArray,"jsarray");
   //for(var j=0;j<mygrid.getRowsNum();j++){  //为新列表附上行id
   //    mygrid.setRowId(j, newArrayId[j]);
   //}
}

处理bug的任务已经完成,为什么还留有这个多注释呢?别急,其实重构才刚刚开始。

刚才称其为重构,其实我们只是选择了一个更合适的方案来代替原来的方案。但是代码中这一串if else语句明显带有坏味道,在性能方面可以使用switch代替;以后要是有其他列数的表格需要排序,我们还得为它再添加一个else if?No,这就是坏味道。

就像《重构 改善既有代码的设计》中提到的某一种程序员,我盯着屏幕开始发呆……

过了几分钟,思路慢慢有了,在过了几分钟,好了,问题解决了。对,我就要删除这些if else语句,下面是再次重构后的代码:

function reCreatMyrightgrid(array,mygrid){   //刷新新的指标列表
   mygrid.clearAll(false);   //不清除表头
   var cellIndicatorArrayLength = mygrid.getColumnsNum()+1;   //单位指标的数组长度(所有列的值加上行id)
   var rowsArray = [];   //存放行数据的数组[{"id":行id,"data":本行每一列的数据数组}]
   var dataAll = {};
   for( var i=0;i<array.length;i=i+cellIndicatorArrayLength){
         var data = [];
         for( var n=0;n<cellIndicatorArrayLength-1;n++){
               data.push(array[i+n]);
         }
         rowsArray.push({"id" :array[i+cellIndicatorArrayLength-1],"data":data});
   }
   dataAll.rows=rowsArray;
   mygrid.parse(dataAll,"json" );
}

到此,对这个方法的重构告一段落。这次重构的效果是很明显的,但从代码量角度看,删除了20/36,最后剩了仅仅16行代码;其次,它解决了很重要的bug;最后它净化了代码,提供了更好的扩展。所以,这次重构是成功的。

是的,逻辑之美就是如此之美!曾记得逻辑之美在我脑海中时,还没有《研究之美》这本以XX之美命名的书,随后可能还会陆续出来其他XX之美的书,而我仅仅等待的是《逻辑之美》。