JavaScript中如何重构嵌套if-else处理日志等横切关注点避免上帝方法?
优化方案
第一步:合并零散参数为配置对象
原来的多布尔参数扩展成本极高,先合并为单个配置对象,同时配置默认值,调用时不需要传全量参数:
const DEFAULT_LOG_OPTIONS = { enableStartLog: false, enableSuccessLog: false, enableFailureLog: false, onlyLogExternal: false, }
fetch 方法入参调整为(url, options = {}),执行时先合并配置:const logOptions = {...DEFAULT_LOG_OPTIONS, ...options}
第二步:抽离统一的日志准入判断逻辑
把散落在各个流程的日志判断逻辑抽为独立函数,消除多层嵌套if:
function shouldLog(url, options, logType) { // 先校验对应类型日志是否开启 if(!options[`enable${logType}Log`]) return false // 校验是否仅允许外部链接日志 if(options.onlyLogExternal && !isExternalLink(url)) return false return true }
原来的多层嵌套判断直接简化为一行:if(shouldLog(url, logOptions, 'Start')) Logger.logFetchStartedEvent()
第三步:用映射表替代错误类型分支判断
错误类型的多if/else分支完全可以用对象映射替代,后续新增错误类型不需要修改核心逻辑:
const ERROR_LOG_MAP = { [TimeOut]: Logger.logFetchFailedTimeOutEvent, [404]: Logger.logFetchFailed404Event, default: Logger.logFetchFailedEvent }
catch块的错误日志逻辑简化为:
if(shouldLog(url, logOptions, 'Failure')) { const logFn = ERROR_LOG_MAP[errorType(error)] || ERROR_LOG_MAP.default logFn() }
第四步:预置常用场景封装,降低调用成本
针对通用场景提前封装专用方法,调用方不需要感知配置细节:
// 全量日志场景 function fetchWithFullLog(url, options = {}) { return fetchApiData(url, { enableStartLog: true, enableSuccessLog: true, enableFailureLog: true, ...options }) } // 仅打印错误日志场景 function fetchWithErrorLogOnly(url, options = {}) { return fetchApiData(url, { enableFailureLog: true, ...options }) } // 仅外部链接打印日志场景 function fetchLogExternalOnly(url, options = {}) { return fetchApiData(url, { enableStartLog: true, enableSuccessLog: true, enableFailureLog: true, onlyLogExternal: true, ...options }) }
重构后完整代码
const DEFAULT_LOG_OPTIONS = { enableStartLog: false, enableSuccessLog: false, enableFailureLog: false, onlyLogExternal: false, } const ERROR_LOG_MAP = { [TimeOut]: Logger.logFetchFailedTimeOutEvent, [404]: Logger.logFetchFailed404Event, default: Logger.logFetchFailedEvent } function shouldLog(url, options, logType) { if(!options[`enable${logType}Log`]) return false if(options.onlyLogExternal && !isExternalLink(url)) return false return true } function fetchApiData(url, options = {}) { const logOptions = {...DEFAULT_LOG_OPTIONS, ...options} if(shouldLog(url, logOptions, 'Start')) { Logger.logFetchStartedEvent() } try { const data = backendApi.get(url) if(shouldLog(url, logOptions, 'Success')) { Logger.logFetchSucceededEvent() } return data } catch (error) { if(shouldLog(url, logOptions, 'Failure')) { const errType = errorType(error) const logFn = ERROR_LOG_MAP[errType] || ERROR_LOG_MAP.default logFn() } } } // 预置场景导出 export { fetchWithFullLog, fetchWithErrorLogOnly, fetchLogExternalOnly }
扩展优势
- 无任何嵌套if/else,所有逻辑平层展开,可读性极强
- 新增日志规则只要修改
shouldLog函数,不需要调整fetch核心流程 - 新增错误类型只要往
ERROR_LOG_MAP加对应映射即可,符合开闭原则 - 新增场景只要加一行预置封装,不需要修改核心逻辑
- 所有配置带默认值,调用方不需要感知无关参数
内容的提问来源于stack exchange,提问作者nexlin
相关产品推荐
相关产品推荐

