圈复杂度
定义
圈复杂度 (Cyclomatic complexity) 是一种代码复杂度的衡量标准,也称为条件复杂度或循环复杂度,它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。简称 CC 。其符号为 VG 或是 M 。
计算规则
for +1; if +1; else +1; switch has n cases +n; || +1; && +1
降低圈复杂度手段
1、类型判断、转换
if (visibleFlag) {
visible = 1;
} else {
visible = 0;
}
改为(如:在数据库保存时无法自动转换数据类型):
visible = visibleFlag + 0
2、独立函数调用
postTitle = postTitle ? postTitle.trim() : '';
改为:
postTitle = util.trim(xss.sanitize(postTitle))
3、多条件合并为一个:转为数组的include等操作
if (action === 'create' || action === 'edit') {...}
改为:
if (['create', 'edit'].includes(action)) {...}
4、空值判断简化
if (page !== undefined && page !== ''){...}
改为
if (!page) {...}
5、嵌套if简化
if (x) {
if (y) {...} else {...}
} else {
...
}
改为:
if (x && y) {...} else {...}
6、复杂if改为使用正则(通配符)
if (/^1[34578]\d{9}$/i.test(phone)) {...}
7、重复判断剥离并复用:赋值给变量、常量,减少二次判断
if (postStatus === 'draft' || postStatus === 'auto-draft') {...}
...
if (postStatus === 'draft' || postStatus === 'auto-draft') {...} else {...}
改为:
const postDraft = postStatus === 'draft' || postStatus === 'auto-draft';
if (postDraft) {...}
降低圈复杂度示例
阈值5。下面这种if判空后赋值有九处(4+5),加上两个err判断,使得原代码圈复杂度为12
func (p *Pipe) getVersionDetails(ctx *context.Context, app *asc.App, version *asc.AppStoreVersion) error {
appLocalizationResp, err := p.Client.GetAppLocalizations(ctx, app.ID)
if err != nil {
return err
}
if appLocalizationResp.Data.Attributes.Name != nil {
ctx.AppStoreVersionDetail.App = *appLocalizationResp.Data.Attributes.Name
log.Info("ctx.AppStoreVersionDetail.App:", ctx.AppStoreVersionDetail.App)
}
if appLocalizationResp.Data.Attributes.Subtitle != nil {
ctx.AppStoreVersionDetail.SubTitle = *appLocalizationResp.Data.Attributes.Subtitle
log.Info("ctx.AppStoreVersionDetail.Subtitle:", ctx.AppStoreVersionDetail.SubTitle)
}
...
...
return nil
}
修改
func (p *Pipe) getVersionDetails(ctx *context.Context, app *asc.App, version *asc.AppStoreVersion) error {
log.Infof("getting app localizations")
appLocalizationResp, err := p.Client.GetAppLocalizations(ctx, app.ID)
if err != nil {
return err
}
InitAppVersionDetailAppInfo(ctx, appLocalizationResp)
...
return nil
}
func InitAppVersionDetailAppInfo(ctx *context.Context,
appLocalizationResp *asc.AppInfoLocalizationResponse) {
InitProp(ctx.AppStoreVersionDetail, "App", appLocalizationResp.Data.Attributes.Name)
InitProp(ctx.AppStoreVersionDetail, "PrivacyPolicyURL", appLocalizationResp.Data.Attributes.PrivacyPolicyURL)
InitProp(ctx.AppStoreVersionDetail, "Subtitle", appLocalizationResp.Data.Attributes.Subtitle)
InitProp(ctx.AppStoreVersionDetail, "PrivacyPolicyText", appLocalizationResp.Data.Attributes.PrivacyPolicyText)
}
func InitProp(appStoreVersionDetail *model.MarketReleaseIosReq,
ctxPropertyName string, respAttribute *string) {
if respAttribute == nil {
return
}
detail := reflect.Indirect(reflect.ValueOf(&appStoreVersionDetail)).Elem()
typeOfT := detail.Type()
for i := 0; i < detail.NumField(); i++ {
if typeOfT.Field(i).Name == ctxPropertyName {
detail.FieldByName(ctxPropertyName).Set(reflect.ValueOf(respAttribute).Elem())
break
}
}
log.Infof("ctx.AppStoreVersionDetail.%+v:%+v", ctxPropertyName, reflect.ValueOf(respAttribute).Elem())
}
|