小步重构:配置优于实现
模拟场景
假设我现在收到一个这样的需求:把Github项目的相关数值进行格式化,以便更好地展示数据。即最终期望效果,如下所示。
注意!以上是模拟数据,非真实数据。
又假设服务端提供的接口数据示例为:
var project = { "name" : "phalapi", "url" : "https://github.com/phalapi/phalpi", "author" : "phalapi", "watch" : 13100, "star" : 21900, "fork" : 3500, "issues" : 1300 };
其中我们需要对watch、star、fork、issues这几个字段的数据进行格式化展示。
最初的实现
在开始之前,先准备一下基础设施函数。这里的格式化很简单,只是用了逗号把数值按千分位进行分割。
function formatNum(num) { return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,'); }
很多同学,估计,第一时间就很快写出以下第一行转换代码。
project['watch'] = formatNum(project['watch']);
这没有什么不对,问题是,当我们需要多个字段进行转换时,我们就明显感觉到在使用“复制-粘贴编程”了。来看一下通常情况下的代码:
project['watch'] = formatNum(project['watch']); project['star'] = formatNum(project['star']); project['fork'] = formatNum(project['fork']); project['issues'] = formatNum(project['issues']);
继而watch字段转换后,我们添加了对剩下的star、fork、issues字段进行转换。
对于此时的project变量,可以看到它已经变成了这样:
{ "name": "phalapi", "url": "https://github.com/phalapi/phalpi", "author": "phalapi", "watch": "13,100", "star": "22,900", "fork": "3,500", "issues": "1,300" }
但对于formatNum()函数我们重复编写了4次,而且每一行字段名重复写了2次。这只是暂时的情况,想一下,如果下次又有新需求,需要对新增的字段pull_requests也进行格式化,猜下结果会是怎样?
估计维护的同学(很可能是你自己),也会参考原来的做法,在后面再加一行:
project['pull_requests'] = formatNum(project['pull_requests']);
这样,问题就来了,到处都是重复的代码,而且如果formatNum()函数改成调用其他的方法,就需要大幅度进行调整修改。
更好的建议
更好的建议是,把这些需要转换的字段进行配置,以便统一管理。这样的重构并不难,稍加调整即可:
var keys = ['watch', 'star', 'fork', 'issues']; for (var it in keys) { project[keys[it]] = formatNum(project[keys[it]]); }
这样,如果遇到新的需求,需要新加一个字段进行格式化,我们只需要简单地配置一下即可。
var keys = ['watch', 'star', 'fork', 'issues', 'pull_requests'];
相比于前面的做法,这里的改动很少,而且基本上是配置式编程,而不是实现式编程。即哪怕是一位新来的实习生,也能很好的胜任这个新需求。
再进一步?
到这里,关于配置优于实现这一小节就已经是完成的了。但我在想,有没有可以连配置也省掉?即做到“智能”的方式,自动进行转换?
就目前的情况来看,是可以的。
for (var it in project) { if (typeof project[it] == 'number') { project[it] = formatNum(project[it]); } }
这样,后续新加了数值型的字段,将能被自动识别并转换,这样连实习生都不需要来参考开发了,更不需要发布、测试,线上自动完美支持。
当然,这样也是需要适用性的。首先,产品需求上允许对全部的数值进行转换;其次,后端接口在返回时要严格要求字段类型。
否则,即使是把事情做正确了,实际上没有做正确的事。
在编程过程中,多思考,总是会有好处的。^_^