别纠结,提高代码整洁度也没那么难( 二 )


下面让我们一起来看一个例子 。Node有一个名为“build-url”的npm模块,用途如其名所示:构建URL 。你可以通过这个链接(https://github.com/steverydz/build-url/blob/master/src/build-url.js)查看源文件 。以下是相关代码 。
function buildUrl(url, options) { var queryString = ; var key; var builtUrl; if (url === ) { builtUrl = ''; } else if (typeof(url) === 'object') { builtUrl = ''; options = url; } else { builtUrl = url; } if (options) { if (options.path) { builtUrl += '/' + options.path; } if (options.queryParams) { for (key in options.queryParams) { if (options.queryParams.hasOwnProperty(key)) { queryString.push(key + '=' + options.queryParams[key]); } } builtUrl += '?' + queryString.join('&'); } if (options.hash) { builtUrl += '#' + options.hash; } } return builtUrl;};请注意,此函数长35行 。虽然理解起来也不是非常难,但如果我们应用“小”原则来重构辅助函数,则会大大简化 。更新和改进后的版本如下 。
function buildUrl(url, options) { const baseUrl = _getBaseUrl(url); const opts = _getOptions(url, options); if (!opts) { return baseUrl; } urlWithPath = _AppendPath(baseUrl, opts.path); urlWithPathAndQueryParams = _appendQueryParams(urlWithPath, opts.queryParams) urlWithPathQueryParamsAndHash = _appendHash(urlWithPathAndQueryParams, opts.hash); return urlWithPathQueryParamsAndHash;};function _getBaseUrl(url) { if (url ===|| typeof(url) === 'object') { return ''; } return url;}function _getOptions(url, options) { if (typeof(url) === 'object') { return url; } return options;}function _appendPath(baseUrl, path) { if (!path) { return baseUrl; } return baseUrl += '/' + path;}function _appendQueryParams(urlWithPath, queryParams) { if (!queryParams) { return urlWithPath } const keyValueStrings = Object.keys(queryParams).map(key => { return `${key}=${queryParams[key]}`; }); const joinedKeyValueStrings = keyValueStrings.join('&'); return `${urlWithPath}?${joinedKeyValueStrings}`;}function _appendHash(urlWithPathAndQueryParams, hash) { if (!hash) { return urlWithPathAndQueryParams; } return `${urlWithPathAndQueryParams}#${hash}`;}你会注意到,虽然我们没有严格遵守每个函数4行的原则,但我们创建了几个相对“较小”的函数 。每个函数仅完成一项任务,你可以根据函数名轻松理解这段代码 。如果需要,你甚至可以针对每个函数进行单元测试,而不是只测试一个大型的buildUrl函数 。你可能还会注意到,这种方法产生的代码略多一些,从35行变成了55行 。这完全可以接受,因为这55行代码比原来的35行更加方便维护和阅读 。
如何才能编写出这样的代码?我个人认为,最简单的方法是列出你希望逐步完成的各项任务 。每一步都可以是建立某个子函数或辅助函数 。例如,针对上述buildUrl函数我们希望完成如下工作:

  1. 初始化baseUrl和options
  2. 添加路径(如果有的话)
  3. 添加查询参数(如果有的话)
  4. 添加锚点(如果有的话)
请注意,上述每一步都可以直接转化为子函数 。一旦养成了这样的习惯,你就可以使用这种自顶向下的方法编写所有代码,然后根据上述步骤列表,建立子函数,再针对每个子函数继续递归,创建步骤列表、建立子函数,以此类推 。
下面再来谈谈单一功能原则 。根据维基百科,单一功能原则的定义如下:
在面向对象编程领域中,单一功能原则(Single Responsibility Principle)规定每个类都应该有一个单一的功能,并且该功能应该由这个类完全封装起来 。所有它的(这个类的)服务都应该严密的和该功能平行(功能平行,意味着没有依赖) 。
在《代码整洁之道》中,Robert Martin给出了另一个定义:


推荐阅读