在《重構 改善既有代碼的設計》一書中,稱一些不完美的,甚至寫的很爛的程序叫做有“壞味道”。當程序有“壞味道”時我們就要對它進行重構。作爲程序猿,做幾次重構後你就會發現你喜歡上重構,不知道別人是不是,反正我是喜歡上重構了。重構是一種對邏輯的審查與修改的過程,在一次做完對一個系統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之美的書,而我僅僅等待的是《邏輯之美》。