在《重构 改善既有代码的设计》一书中,称一些不完美的,甚至写的很烂的程序叫做有“坏味道”。当程序有“坏味道”时我们就要对它进行重构。作为程序猿,做几次重构后你就会发现你喜欢上重构,不知道别人是不是,反正我是喜欢上重构了。重构是一种对逻辑的审查与修改的过程,在一次做完对一个系统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]);
}
}
* 功能:下面方法功能是对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]);
//}
}
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" );
}
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之美的书,而我仅仅等待的是《逻辑之美》。