网站开发面板昆明seo博客南网站建设
news/
2025/9/27 3:43:43/
文章来源:
网站开发面板,昆明seo博客南网站建设,怎样用电脑和网訨自己做网站,辽宁省建设厅证书查询网站网上有个段子#xff0c;说建筑工程师不会轻易答应会给摩天大楼增加一个地下室#xff0c;但代码开发工程师却经常在干这样的事#xff0c;并且总有人会对你说“这个需求很简单”。到土里埋个雷#xff0c;这确实不复杂#xff0c;但我们往往面临的真实场景其实是“在一片…
网上有个段子说建筑工程师不会轻易答应会给摩天大楼增加一个地下室但代码开发工程师却经常在干这样的事并且总有人会对你说“这个需求很简单”。到土里埋个雷这确实不复杂但我们往往面临的真实场景其实是“在一片雷区的土里埋一个雷”。而雷区里哪里有雷任何人都不知道。
回到我们日常的写代码的场景我们一直在说系统很复杂那到底什么是系统复杂度呢最近几个月蚂蚁代码力平台注是蚂蚁的代码评价平台进入大家视野很多同学开始关注起自己代码力的得分情况。作为团队的稳定性底盘负责人也经常和大家探讨为什么会因为圈复杂度高而被扣分。那么怎么才能写的一手可读可扩展可维护[注1]的好代码本文作者尝试结合在团队内部的实践分享下过程中心得希望对大家的代码圈复杂度治理提供微弱的帮助。
什么是圈复杂度
先看看圈复杂度的通用的定义圈复杂度Cyclomatic complexity简写CC[注2]也称为条件复杂度/循环复杂度是一种代码复杂度的衡量标准。由托马斯·J·麦凯布Thomas J. McCabe, Sr.于1976年提出用来表示程序的复杂度其符号为VG。它可以用来衡量一个模块判定结构的复杂程度数量上表现为独立现行路径条数也可理解为覆盖所有的可能情况最少使用的测试用例数。说人话圈复杂度关系到质量同学最少需要设计多少用例才能覆盖你的代码分支。
怎么计算圈复杂度
蚂蚁广目平台给出了比较详细的说明这里直接引用网上也可以查到类似内容。
节点判断计算公式为V (G) P 1 注除了节点判断法还有其他方法如点边判断法这里只选一个用于说明。
其中P为条件节点数条件节点类型为
a.条件语句
if语句while语句包含do...while...语句for语句包含foreach语句switch case语句try catch语句
b.条件表达式二元或多元 表达式|| 表达式三元运算符举例如下(部分代码省略后用xxx代替)
//案例1圈复杂度V(G) 1(if) 1(catch) 1 3
public String myMethod1(){if(xxx){try {//xxx;} catch (IOException e) {//xxx;}}else{xxx;}return xx;
}//案例2圈复杂度V(G) 2(if) 1() 1 4
public String myMethod2() {if (xxx) {//xxx;} else {if (xxx xxx) {//xxx;} else {//xxx;}xx();}return xx;
}
为什么要关注圈复杂度
好了了解了圈复杂度的定义之后我们基本可以得出一个结论圈复杂度大说明程序逻辑复杂不利于代码的阅读维护和后续扩展。如果需要看懂一个圈复杂度高的方法需要小心翼翼整理所有的分支情况而改动这类代码更像踏入雷区一样。
下面我们来看一段代码案例部分内容已省略
public XXresult doSave( XXDTO newScriptDTO) {String type Enums.ScriptType.CUSTOM;Boolean containsTryCatch StringUtil.contains(content, try) StringUtil.contains(content, catch);if (StringUtil.isBlank(scriptName)) {baseOperationResult.setMessage(XXX);return baseOperationResult;}if (!scriptName.matches(^[(\\d)|_|a-z|A-Z]$)) {baseOperationResult.setMessage(XXX);return baseOperationResult;}NewScript tempScript null;try {tempScript newScriptManager.findByName(StringUtil.trim(scriptName));} catch (Exception e) {baseOperationResult.setMessage(XXX);return baseOperationResult;}if (StringUtil.isBlank(id)) {if (tempScript ! null) {baseOperationResult.setMessage(XXX);return baseOperationResult;}} else {Integer editScriptId Integer.parseInt(id);if (null ! tempScript) {if (!editScriptId.equals(tempScript.getId())) {baseOperationResult.setMessage(XXX);return baseOperationResult;}}}if (!Enums.NewScriptTypeEnum.XX.contains(scriptType)) {baseOperationResult.setMessage(XX);return baseOperationResult;}Boolean needSubtypeMode true;if (StringUtils.equals(scriptType, Enums.XX.XX)|| StringUtils.equals(scriptType, Enums.XX.PRE)) {needSubtypeMode false;}NewScript script new NewScript();script.setScriptType(scriptType);if (StringUtil.isNumeric(status)) {script.setStatus(Integer.parseInt(status));}if (StringUtil.isNotBlank(scriptCategory)) {script.setScriptCategory(ScriptCategory.getByCode(scriptCategory));}String subType ;if (needSubtypeMode) {if (StringUtil.isBlank(subtypeandtip)) {baseOperationResult.setMessage(XXX);return baseOperationResult;}}if (needSubtypeMode) {List NewScript allActiveAndTestRunScripts newScriptManager.findAllActiveAndTestRunScripts();List String allActiveAndTestRunSubTypeList new ArrayList();for (NewScript activeAndTestRunScript : allActiveAndTestRunScripts) {List String subTypeListEveryScript Arrays.asList(Optional.ofNullable(activeAndTestRunScript.getSubType()).orElse(new String()).split(,));for (String subTypeTemp : subTypeListEveryScript) {if (StringUtil.isNotBlank(subTypeTemp)) {allActiveAndTestRunSubTypeList.add(subTypeTemp);}}}try {JSONArray subtypetipsArray JSON.parseArray(subtypeandtip);if (StringUtil.isBlank(id)) {for (Object object : subtypetipsArray) {JSONObject subtypetipsObject (JSONObject) object;String subtypeSingle subtypetipsObject.getString(subtype);if (StringUtil.isBlank(subtypeSingle)) {baseOperationResult.setSuccess(false);return baseOperationResult;}if (CollectionUtils.contains(allActiveAndTestRunSubTypeList.iterator(),subtypeSingle)) {baseOperationResult.setSuccess(false);return baseOperationResult;}}} else {if (1.equals(status) || 2.equals(status)) {for (Object object : subtypetipsArray) {//省略部分内容XXX;if (StringUtil.isBlank(subtypeSingle)) {baseOperationResult.setSuccess(false);return baseOperationResult;}for (NewScript oldNewScript : allActiveAndTestRunScripts) {if (oldNewScript.getId().equals(Integer.parseInt(id))) {continue;}//省略部分内容XXX;if (CollectionUtils.contains(filtered.iterator(), subtypeSingle)) {baseOperationResult.setSuccess(false);return baseOperationResult;}}}}}for (Object object : subtypetipsArray) {if (1 script.getStatus() || 2 script.getStatus()) {SubtypeTips subtypeTips null;subtypeTips subtypeTipsManager.findBySubtype(subtypeSingle);if (subtypeTips null) {subtypeTips new SubtypeTips();}subtypeTips.setSubtype(subtypeSingle);subtypeTips.setInternalTips(innertips);subtypeTips.setExternalTips(externaltips);subtypeTips.setShareLink(shareLink);subtypeTips.setStatus(1);subtypeTipsManager.save(subtypeTips);}}subType StringUtil.substring(subType, 0, subType.length() - 1);} catch (Exception e) {baseOperationResult.setSuccess(false);baseOperationResult.setMessage(XXX);return baseOperationResult;}}boolean needCreateTestRunScript false;if (StringUtils.isNotBlank(id)) {script.setId(Integer.parseInt(id));NewScript orgin newScriptManager.findById(Integer.parseInt(id));if (null ! orgin 1 orgin.getStatus() 1.equals(status)) {if (StringUtil.isNotBlank(orgin.getContent())) {String originContentHash CodeUtil.getMd5(StringUtil.deleteWhitespace(orgin.getContent()));String contentHash CodeUtil.getMd5(StringUtil.deleteWhitespace(content));if (!StringUtil.equals(originContentHash, contentHash)) {needCreateTestRunScript true;}}}} else {script.setSubmitter(user.getLoginName());}Set String systemList new HashSet String();if (StringUtil.isNotBlank(systems)) {String[] systemArray systems.split(,);for (int i 0; i systemArray.length; i) {systemList.add(systemArray[i]);}}if (needCreateTestRunScript) {if (needSubtypeMode) {content replaceContent(content, subType);String testScriptSubType ;List String subTypeList Arrays.asList(StringUtil.split(subType, ,));for (int i 0; i subTypeList.size(); i) {testScriptSubType this.UPDATE_SCRIPT subTypeList.get(i);if (i ! subTypeList.size() - 1) {testScriptSubType ,;}}subType testScriptSubType;}scriptName this.UPDATE_SCRIPT scriptName;NewScript oldUpdateScript newScriptManager.findByName(scriptName);if (null ! oldUpdateScript)script.setId(oldUpdateScript.getId());else {script.setId(null);}baseOperationResult.setNeedAudit(true);}if (StringUtil.isBlank(fileSuffix)) {//如果全空的话 默认全扫script.setSuffix(.*);} else {script.setSuffix(fileSuffix);}script.setName(scriptName);if (StringUtil.equals(allPath, Y)) {script.setAllPath(Y);} else {script.setAllPath();}script.setEnvTag(tenantScope);script.setNeedAutoScan(needAutoScan);if (StringUtil.isNotBlank(scopes)) {for (String each : StringUtil.split(scopes, ,)) {each StringUtil.replace(each, , );script.addScope(each);}}if (StringUtil.isNotBlank(content)) {BaseOperationResult preLoadResult syntaxCheck(script);if (!preLoadResult.isSuccess()) {baseOperationResult.setMessage(preLoadResult.getMessage());return baseOperationResult;}}if (StringUtil.contains(content, new Bug)) {baseOperationResult.setSuccess(false);return baseOperationResult;}try {Result NewScript result newScriptManager.saveCustomScript(script);if (result.isSuccess()) {if (EnvUtil.isProdEnv() EnvUtil.isLinux()) {if (!needCreateTestRunScript) {//省略部分内容XX} else {//省略部分内容XX}}Boolean hasOldScript processOldEngineRule(scriptName);if (containsTryCatch) {if (hasOldScript) {//省略部分内容XX} else {//省略部分内容XX}} else {if (hasOldScript) {baseOperationResult.setMessage(XXX);} else {baseOperationResult.setMessage(保存成功!);}}baseOperationResult.setId(script.getId());processTenantRelation(script.getId(), tenantIdList, user.getLoginName());if (!needCreateTestRunScript needSubtypeMode (StringUtil.equals(Enums.XX.COMMON, script.getScriptType())|| (StringUtil.equals(Enums.XX.SCRIPT,script.getScriptType())))) {JSONArray subtypetipsArray JSON.parseArray(subtypeandtip);for (Object object : subtypetipsArray) {//省略部分内容XX}}} else {baseOperationResult.setSuccess(false);return baseOperationResult;}} catch (Exception e) {baseOperationResult.setMessage(XX);}return baseOperationResult;}
原代码大概400行以上复杂度69憋了一口长气才读完。如果让你来接手这段代码是不是感觉很头疼需要梳理里面各种分支逻辑弄清楚主干脉络。
那么什么样的代码才容易读,容易上手呢一般业界认为代码可读性可测试维护成本和圈复杂度有很大关系具体如下 我该怎么做
1.【知己知彼了解自己代码复杂度】这个比较简单有以下几种方式
a.自己数下判定节点if while for catch case and or等大概就知道圈复杂度是多大了参考上面怎么计算圈复杂度章节。
b.在蚂蚁内部使用的广目平台也可以查看到新提交commit记录里哪些方法圈复杂度比较高。 c.在代码提交之前自己用idea小插件Metrics Reloaded插件一次性扫描自己负责的系统所有方法的复杂度。 红色部分标识圈复杂度数字越大复杂度越高。
2.【对症下药降低复杂度】网上有很多方法我总结了下大概有以下几种
方法一抽取出独立逻辑的子方法把复杂逻辑拆分成几个独立模块再去读代码就会感觉清晰很多。以上面举例的复杂度69的方法为例我们做了如下的方法拆分是不是感觉清晰了很多
public XXresult doSave( NewScriptDTO newScriptDTO) {//0.构造结果XXresult resultnew XXresult() ;try{//1.脚本名检查scriptNameCheck(newScriptDTO); //2.脚本加载loadScript(newScriptDTO); //3.脚本保存saveScript(newScriptDTO); }catch(XXException e){result.setSuccess(false)result.setMessage(XXX);return result;}catch(Exception e){result.setSuccess(false)result.setMessage(XXX);return result;}//操作完成result.setSuccess(true)result.setMessage(XXX);return result}
/**检查脚本名*/
private void scriptNameCheck(NewScriptDTO newScriptDTO){xxx
}
/**加载脚本*/
private void loadScript(NewScriptDTO newScriptDTO){xxx
}
/**保存脚本*/
private void saveScript(NewScriptDTO newScriptDTO){xxx
}
方法二优化逻辑判断通过提取频繁出现的条件 或者调整判断顺序等方式达到简化代码目的。
/ 案例1抽取频繁出现的条件a/
//修改前
if (条件1)
{if (条件a){// 执行a逻辑}
}
else if(条件2)
{if (条件a){// 执行b逻辑}
}
if (条件a)
{// 执行c逻辑
}
//修改后
if (条件a)
{if (条件1){// 执行a逻辑}else if(条件2){// 执行b逻辑} // 执行c逻辑
}
/ 案例2优化逻辑判断顺序/
//修改前
if条件1 条件2|| !条件1{return true;
}else{return false;
}
//修改后
if(条件1 条件2){return false;
}
return true;
方法三适当使用java新特性降低大量的if判断。下面是来自团队一淏同学的提供的优化案例
//修改前List list XXX;if (CollectionUtils.isEmpty(list)) {for (XX item : list) {if (itemnull){return;}else{// 逻辑a}}//修改后List list XX;list Optional.ofNullable(list).orElse(new ArrayList());list.stream().filter(Objects::nonNull).forEach(item-{//逻辑a});}
当然只要用心钻研降低复杂度还有很多方法这里不一一列举了。总结下思路
一个方法/类不要写大段大段的代码把内容封装在逻辑独立的子类和子方法里。采用有意义的类名方法名让使用者见名思意易于上手。逻辑表达上优化判断逻辑成最简形式。适当使用编程技巧合并判断方式。
结语
作为蚂蚁工程师的我们开发代码也应该像创作一个艺术品深思熟虑精雕细刻经过产品的不断升级迭代仍然能够保持顽强的生命力就像代码四层境界[注3]里面说的第四层经过了时间历练“我的代码还在用”。
引用
[注1]对代码的领悟之-高质量代码有三要素可读性、可维护性、可扩展性 https://wenku.baidu.com/view/ce7e54e60f22590102020740be1e650e52eacff5.html
[注2]详解圈复杂度https://baike.baidu.com/item/%E5%9C%88%E5%A4%8D%E6%9D%82%E5%BA%A6/828737
[注3]代码的四层境界
https://www.sohu.com/a/238434622_185201 第一层“我的代码写完了”第二层“我的代码写好了”第三层“我的代码能跑了”第四层“我的代码还在用”
作者 | 陈胜利(李渔)
原文链接
本文为阿里云原创内容未经允许不得转载。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/916810.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!