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