无论在iPhone开发还是学习的过程中都会看到一些不是很理想的代码,不可否认自己也在不断“贡献”着这类代码。面对一些代码的“坏味道”,重构显然是个有效的解决途径。《iPhone开发重构》系列就想总结和补充iPhone开发中经历的一些重构,其间可能会引用一些开源以及实际项目的代码,本着对技术的探求,冒昧之处还请作者多多见谅。
记得刚开始做软件开发的时候,我的导师就在一次函数设计的时候说:“函数粒度很重要,但即使我做了接近二十年的软件,有时也无法很好把握粒度。这点就只可意会不可言传了。”这句话可能一部分是出自谦虚,但更多是道出软件开发的规律。当时我们无法去理解,现在开始慢慢理解。我们设计一个函数的时候,可能有时就按需设计了,比如需要获取一个对年龄的文字提示的时候就设计一个getAgeTip()的函数,实现代码如下:
重构前:
NSString* getAgeTip(int nBornYear, int nBornMonth, int nBornDay)
{
NSString* strRet = (@"");
int nAge = 0;
int nYear = ServerDate.serverYear;
int nMonth = ServerDate.serverMonth;
int nDay = ServerDate.serverDay;
nAge = nYear - nBornYear - 1;
if ((nMonth > nBornMonth) || (nMonth == nBornMonth && nDay > nBornDay)) {
++nAge;
}
nAge = MAX(nAge, 0);
if (nAge > 0 && nAge < 200) {
strRet = [NSString stringWithFormat:@"%d", nAge];
if (bWithUnit) {
strRet = [strRet stringByAppendingString:NDSTR(@"岁")];
}
}
return strRet;
}
但随着开发的深入以及需求的变更,可能就会有一个获取年龄的需求。这时候就会发现原来getAgeTip()函数中就有获取年龄的逻辑,粒度太大了,无法实现复用。面对这种问题的时候就需要重新调整函数粒度,通过提取getAge()方法后重构代码如下:
重构后:
NSString* getAgeTip(int nBornYear, int nBornMonth, int nBornDay)
{
NSString* strRet = (@"");
int nAge = getAge(nBornYear, nBornMonth, nBornDay);
if (nAge > 0 && nAge < 200) {
strRet = [NSString stringWithFormat:@"%d", nAge];
if (bWithUnit) {
strRet = [strRet stringByAppendingString:NDSTR(@"岁")];
}
}
return strRet;
}
int getAge(int nBornYear, int nBornMonth, int nBornDay)
{
int nAge = 0;
int nYear = ServerDate.serverYear;
int nMonth = ServerDate.serverMonth;
int nDay = ServerDate.serverDay;
nAge = nYear - nBornYear - 1;
if ((nMonth > nBornMonth) || (nMonth == nBornMonth && nDay > nBornDay)) {
++nAge;
}
nAge = MAX(nAge, 0);
return nAge;
}
其中,对内getAgeTip()调用了getAge(),对外用户可以根据需要调用getAgeTip()或者getAge()。粒度细并合理的情况下,代码的复用性和可读性就大大提供了。但也不能太细了,这样函数的数目就会“爆炸”从而影响了代码维护。粒度的拿捏很重要,但却很难,这需要在实践中慢慢体会。