如何避免多if/else并重构ngOnChanges将认知复杂度降至15?
降低认知复杂度&重构ngOnChanges的方案
一、通用方法:减少if/else、降低认知复杂度
认知复杂度衡量的是代码的理解难度,核心关联分支嵌套、流程跳转的频次。要降低它,核心是让逻辑更扁平、单一职责:
- 用默认值替代存在性判断:用
obj.key ?? 默认值或解构赋值带默认值,替代if (!obj.hasOwnProperty('key'))这类重复判断 - 拆分逻辑到独立函数:把不同功能块抽成小函数,每个函数只做一件事,避免大函数里堆一堆分支
- 简化嵌套条件:把嵌套的if拆成扁平判断,或用提前返回减少嵌套层级
- 用对象映射替代多分支if/else:如果是根据不同值执行不同操作,把对应关系存在对象里,直接通过键取值调用,替代一堆else if
二、针对你的ngOnChanges代码的重构步骤
你的代码里大量重复使用hasOwnProperty判断,加上嵌套分支,导致认知复杂度居高不下。下面是具体重构方案:
步骤1:提前合并chartData默认值
先给chartData补全默认值,避免后续重复做存在性判断:
const defaultChartData = { rotation: -90, circumference: 360, cutoutPercentage: 90, legendPosition: "bottom", chartTitleFontSize: 20, aspectRatio: 3, }; // 合并用户传入的配置和默认值 this.chartData = { ...defaultChartData, ...this.chartData };
步骤2:抽离颜色配置逻辑
把颜色相关的判断拆成独立函数,单一职责更清晰:
private getChartColors(): Array<{ backgroundColor?: string[] }> { if (this.chartData.color) { return [this.chartData.color]; } return [ { backgroundColor: [ LEDSColorPalette.purple[500], LEDSColorPalette.purple[50], ] } ]; }
步骤3:拆分options构建逻辑
把options的构建拆成多个小函数,每个子函数负责一个细分功能:
// 处理tooltips回调 private buildTooltipsCallbacks() { if (!this.chartData.tooltipsCallbacks) return {}; const callbacks: Record<string, Function> = {}; Object.keys(this.chartData.tooltipsCallbacks).forEach(key => { const config = this.chartData.tooltipsCallbacks[key]; callbacks[key] = new Function(config.arguments, config.body); }); return callbacks; } // 处理中心文本格式 private processCenterText() { if (!this.chartData.centerText) return false; let centerText: Array<{ text: string, font?: any, color?: string }> = []; // 统一转为数组对象格式 if (typeof this.chartData.centerText === 'string') { centerText = [{ text: this.chartData.centerText }]; } else if (Array.isArray(this.chartData.centerText)) { centerText = this.chartData.centerText.map(elem => typeof elem === 'object' ? elem : { text: elem } ); } else if (typeof this.chartData.centerText === 'object') { centerText = [this.chartData.centerText]; } // 补全文本默认配置 return centerText.map((label, index) => { label.text = String(label.text); label.font = label.font || {}; label.font.family = label.font.family || '"Myriad Set Pro",Arial'; if (index === 0) { label.font.size = label.font.size || "40"; label.color = label.color || "#000"; } return label; }); } // 构建完整的options配置 private buildChartOptions() { const centerText = this.processCenterText(); return { rotation: this.chartData.rotation * (Math.PI / 180), responsiveAnimationDuration: 3000, maintainAspectRatio: false, circumference: this.chartData.circumference * (Math.PI / 180), responsive: true, cutoutPercentage: this.chartData.cutoutPercentage, tooltips: { enabled: this.chartData.tooltips ?? false, callbacks: this.buildTooltipsCallbacks() }, animation: { duration: 3000 }, legend: { position: this.chartData.legendPosition, labels: { usePointStyle: true } }, title: { display: this.chartData.chartTitle, text: this.chartData.chartTitle, fontSize: this.chartData.chartTitleFontSize, fontColor: this.chartData.chartTitleFontColor, }, plugins: { dataLabels: this.chartData.dataLabels ?? false, doughnutlabel: centerText ? { labels: centerText } : false, }, layout: this.chartData.layout || {}, aspectRatio: this.chartData.aspectRatio, }; }
步骤4:重构后的ngOnChanges
现在ngOnChanges变得极度简洁,逻辑一目了然:
ngOnChanges() { // 基础数据赋值 this.dataset.data = this.chartData.data; this.labels = this.chartData.labels; this.legend = this.chartData.legend; this.chartStyle = this.chartData.chartStyle || {}; // 处理数据缺失场景 if (this.chartData.dataPresent === false) { this.dataAbsent = true; this.chartStyle = { display: "none" }; } // 合并默认配置 const defaultChartData = { rotation: -90, circumference: 360, cutoutPercentage: 90, legendPosition: "bottom", chartTitleFontSize: 20, aspectRatio: 3, }; this.chartData = { ...defaultChartData, ...this.chartData }; // 赋值颜色和最终配置 this.colors = this.getChartColors(); this.options = this.buildChartOptions(); }
重构后的效果
- 所有嵌套分支被拆成扁平的独立函数,每个子函数的认知复杂度都远低于15
- ngOnChanges本身仅负责调用各子函数,认知复杂度降到个位数
- 代码职责划分清晰,新人接手时能快速定位各功能模块
内容的提问来源于stack exchange,提问作者rahul-kola
相关产品推荐
相关产品推荐

