1 / 22

微型重构-解决代码嵌套问题

微型重构-解决代码嵌套问题. 1000COPY 真懂得,讲出来 1000COPY@GMAIL.COM. 故事 1. 我的飞碟铸件 2. 一次做对?. 引入: 1. 评审,少量编码。 分销项目组一个月,第一次并组开发 2. 微观优化 if else,for ,while的深度嵌套 3. 真实例子 4. 力荐一种适合我们的方法==》 干掉嵌套 模式1 . 异常+函数==》if嵌套 模式2 优先序判断函数 ==> 多层简单嵌套 模式3 递归 ==> 多层for/while嵌套. 问题

nash
Download Presentation

微型重构-解决代码嵌套问题

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 微型重构-解决代码嵌套问题 1000COPY 真懂得,讲出来 1000COPY@GMAIL.COM

  2. 故事 1. 我的飞碟铸件 2. 一次做对?

  3. 引入: 1. 评审,少量编码。分销项目组一个月,第一次并组开发 2. 微观优化 if else,for ,while的深度嵌套 3. 真实例子 4. 力荐一种适合我们的方法==》 干掉嵌套 模式1. 异常+函数==》if嵌套 模式2 优先序判断函数 ==> 多层简单嵌套 模式3 递归 ==> 多层for/while嵌套

  4. 问题 1. TDD,敏捷,CMMI,RUP,OO? 2. 回到基础来

  5. Winberg 咨询师《理解专业程序员》 “评审了几十家机构的几百份程序,所有的程序都体现了大致相同的问题” 编码问题的第一条就是代码的分支和嵌套问题。 我们的很多代码常常一个函数动辄上千行,不断的嵌套,形成一个巨大的锯齿形代码,让阅读测试都成为一个问题。这个问题依然存在。 设计问题。提到代码中对输入参数进行合法性检查; 我们的代码很多根本没有考虑这个问题。 便利特性。即使评估了当年两个教授的公开出版的样本代码,也有很多新近 引入的便利特性--可以让代码更加容易阅读,更高效率的特性--没有使用。 这本书应该写于1970's, 。我想如果我们有机会去做这样的一个评审,从统计学上看应该结果雷同。

  6. 问题:删除草稿的代码 DEMO . if (ReadSysCon(syscon_DraftDelTs)) and ( not isBatchAuditing) then //需要给出询问提示 Begin if SuperMessageBox(pchar('是否删除草稿?'), '提示', mbtInformation, mbbOKCancel) = idok then begin if (CheckPowerNoDialog('0902','038')) then //检查是否有删除草稿权限 begin if (BillOperatorID<>CurrOperatorID) then //操作员是否为制单人 begin if (CheckPowerNoDialog('0902','078')) then //是否有删除他人草稿权限 begin ExecuteProcByName('F_D_DeleteDly', ['@dbname', '@vchcode'], ['bakdly', o_indexid], nil); ExecuteProcByName('F_D_DeleteDlyndx', ['@vchcode'], [o_indexid], nil); SendMessageToAllWindow(WM_SETTLEDCOMPLETE, lParam(StringToInt(o_indexid)), 0); end else Begin Application.MessageBox('你没有单据中心业务草稿的删除他人草稿的权限,无法删除!','提示',48); end; end else begin ExecuteProcByName('F_D_DeleteDly', ['@dbname', '@vchcode'], ['bakdly', o_indexid], nil); ExecuteProcByName('F_D_DeleteDlyndx', ['@vchcode'], [o_indexid], nil); SendMessageToAllWindow(WM_SETTLEDCOMPLETE, lParam(StringToInt(o_indexid)), 0); end; end else Begin Application.MessageBox('你没有单据中心业务草稿的删除的权限,无法删除!','提示',48); end; end; end 1. 5层嵌套。不容易阅读,逻辑复杂 2. 很多这样的代码 3. 32行

  7. 1. if return ,先判定不符合条件的,让它退出 refactor : replace nested loop with guard statement . M. Fowler 2. supermessage ->confirm 3. 共同逻辑合并 4. 魔术数字 5. 单条语句不必用begin end块 6. 两个messagebox完全相同,可以合并 7. 条件不符的根本不必提示“是否删除” 8. 第二个if和第三个if可以合并 9. 第一个if可以not,直接退出,这样减少一个嵌套 10. 两个数据库操作应该用事务

  8. 老张提供的例子 if then 几公里的代码 else 几公里的相同代码,略微做修改 end 不易阅读 不好维护

  9. 改了后: if (ReadSysCon(syscon_DraftDelTs)) and ( not isBatchAuditing) then Begin if SuperMessageBox(pchar('是否删除草稿?'), '提示', mbtInformation, mbbOKCancel) = idok then begin checkError(!CheckPowerNoDialog('0902','038'),'你没有单据中心业务草稿的删除的权限,无法删除!") checkError(BillOperatorID<>CurrOperatorID && !CheckPowerNoDialog('0902','078'),'你没有单据中心业务草稿的删除他人草稿的权限,无法删除!') ExecuteProcByName('F_D_DeleteDly', ['@dbname', '@vchcode'], ['bakdly', o_indexid], nil); ExecuteProcByName('F_D_DeleteDlyndx', ['@vchcode'], [o_indexid], nil); SendMessageToAllWindow(WM_SETTLEDCOMPLETE, lParam(StringToInt(o_indexid)), 0); end; 1. 2层vs 5层嵌套,好多了 2. 9行 vs 32行 还可以继续 SuperMessageBox()

  10. 方法 1. 利用异常的特性,化嵌套为直线代码 这里在checkerror内使用了abort异常的特性。 Function checkError(cond,msg) If (cond) begin ShowMessage(msg);abort;end;end; 通过利用异常的特性,把嵌套代码变成直线代码。 2. 抽取公用代码

  11. More 1. 除掉魔术数字和魔术字符串。比如这样的'0902','078',都是魔术字符串。应该用枚举和常量替代。 2. 执行存储过程需要加入事务。

  12. 在一个例子:同样的问题和同样的方法 if (r == -301) throw new Exception( string.Format( "该{0}【{1}】存在经营历程或已经在职员信息中使用,不能分类!", "部门", parFullName)); else if (r == -501) throw new Exception(string.Format(UsedError, "订单")); else if (r == -502) throw new Exception(string.Format(UsedError, "借欠单")); else if (r == -503) throw new Exception(string.Format(UsedError, "维修单")); else throw new Exception(string.Format("操作失败!返回码{0}", r));

  13. 同样的问题和同样的方法 CheckError(r == -301,"该{0}【{1}】存在经营历程或已经在职员信息中使用,不能分类!",typeName,parFullName); CheckError(r == -501, UsedError, "订单"); CheckError(r == -502, UsedError, "借欠单"); CheckError(r == -503, UsedError, "维修单");

  14. 适用性 只要有异常处理就可以采用 vb on error js try catch c# try catch delphi try catch 写一个checkError的函数。checkError(bool cond,msg)

  15. 使用历史 easycmd 366club 分销 erp5.0 尝试分析服装版

  16. 模式2 - 优先序判断函数 ==> 多层嵌套 if ( pricetrack.toString() == "1"){ //使用价格跟踪 if (trackprice){//有跟踪价 rowData.price = trackprice; rowData.taxprice = rowData.price * (1 + rowData.tax/100); this._round(rowData,"taxprice",4); } else{//没有跟踪价 if (selectedData.recprice){//有最近进价 rowData.price = selectedData.recprice; rowData.taxprice = selectedData.recprice * (1 + rowData.tax/100); this._round(rowData,"taxprice",4); } else{//没有最近进价 if (selectedData.preprice5){ //有预设进价 rowData.price = selectedData.preprice5; rowData.taxprice = selectedData.preprice5 * (1 + rowData.tax/100); this._round(rowData,"taxprice",4); } else{ //没有预设进价 rowData.price = ""; rowData.taxprice = ""; } } } }

  17. 模式2 - 优先序判断函数 ==> 多层嵌套 if ( pricetrack.toString() == "1"){ rowData.price = getFirstNotNull([tracePrice,selectedData.recprice,selectedData.preprice5,""]); if (rowData.price!="")){ rowData.taxprice = rowData.price * (1 + rowData.tax/100); this._round(rowData,"taxprice",4); } } 2层 vs 4层 引入函数,好读

  18. 模式3 - 利用递归 ==> 多层嵌套 函数功能:删除数组中.sEname中为空的元素。 procedure TfrmCommonFilter.DecEmptyItem; var i,j,k,l: Integer; begin k := 0; for I:= Low(ArrFilter) to High(ArrFilter) do begin if ArrFilter[i].sEName = '' then begin l := 0; while ArrFilter[i].sEName = '' do begin if l + i >= High(ArrFilter) then begin if l > k then k := l; Break; end; Inc(l); for j := i to High(ArrFilter) - 1 do begin ArrFilter[j].sEName := ArrFilter[j + 1].sEName; ArrFilter[j].sCName := ArrFilter[j + 1].sCName; ArrFilter[j].sItemType := ArrFilter[j + 1].sItemType; ArrFilter[j].iFlag := ArrFilter[j + 1].iFlag; end; ArrFilter[High(ArrFilter)].sEName := ''; end; end; end; SetLength(ArrFilter,High(ArrFilter) - k ); end;

  19. 模式3 - 利用递归 ==> 多层for,while嵌套 函数功能:删除数组中.sEname中为空的元素。 DeleteEmpty(ArrFilter,0); function DeleteEmpty(ArrFilter,FromIndex) begin var index = FindFirstEmpty(ArrFilter,FormIndex); if (index <> -1 ) begin MoveForward(ArrFilter,index) DeleteEmpty(ArrFilter,index) End end function MoveForward(ArrFilter,to) begin for j := from to High(ArrFilter) - 1 do begin ArrFilter[j].sEName := ArrFilter[j + 1].sEName; ArrFilter[j].sCName := ArrFilter[j + 1].sCName; ArrFilter[j].sItemType := ArrFilter[j + 1].sItemType; Arrer[j].iFlag := ArrFilter[j + 1].iFlag; end; end 1层嵌套 vs 4层嵌套 3层循环 vs 无循环 ;递归特别适合解决嵌套循环的代码。可以得到比较神奇的效果。遇到这一情况,试试递归。 30 vs 14 行

  20. 模式3 - 利用递归 ==> 多层嵌套 衍生效果: 对于需要删除的数据结构,尽量不要选择数组。如果是链表,一次循环即可。

  21. 重点推荐 模式1

  22. 重点推荐 模式1 重点推荐 模式1 Edsger Wybe Dijkstra 这个娃 1. structure programming 2. goto statement considered harmful 3. The Humble Programmer 程序员必须知道谦逊,方能 在九个数量级的复杂度上有所成就

More Related