编写短小的代码单元
代码单元的长度应<=15行,将长的代码分解成多个更短的代码单元;短小的代码单元易于理解、测试和重用。
代码单元:可独立维护和执行的最小代码集合。
例如在C#中,一个方法或者构造函数就是一个代码单元。
短小的代码一般是只有一个职责,比如列举的例子有一个方法,根据URL中的一个客户标识,生成该客户的所有银行账户以及收支明细列表。
方法体伪代码如下:
{
处理一个HTTP GET请求
访问数据库并返回所需数据
银行账户的校验
拼接返回的JSON结果
}
这个方法就不是短小的代码单元,可以分解为4个短小的单元。
每个职责一个单元,就更容易测试,也更容易分析代码内容。短小的代码单元更加通用,也就意味着更容易重用。
提取方法,作为一个技巧来达到满足短小代码单元的原则。
示例代码是一个JPanman(吃豆人游戏),游戏界面上目前只有Start和Stop两个按钮。此处针对start方法举例,
public void Start()
{
if(inProgress)
{
return;
}
inProgress=true;
//下面是增加的新功能
//如果玩家死亡则更新观察者
...
//如果所有的豆被吃光则更新观察者
...
}
此时该方法总共21行,这时就需要考虑能否减到15行以内
提取方法,就是把新加的功能提炼成一个方法,改造后如下
public void Start()
{
if(inProgress)
{
return;
}
inProgress=true;
UpdateObservers(); //提取的新方法
}
private void UpdateObservers()
{
//如果玩家死亡则更新观察者
...
//如果所有的豆被吃光则更新观察者
...
}
这样分解为了2个短小的代码单元,但是UpdateObservers方法还可以再分解,
private void UpdateObservers()
{
UpdateObserversPlayerDied();
UpdateObserversPelletsEaten();
}
private void UpdateObserversPlayerDied()
{
//如果玩家死亡则更新观察者
...
}
private void UpdateObserversPelletsEaten()
{
//如果所有的豆被吃光则更新观察者
...
}
这样虽然总代码行数增加了,但是每个代码单元更短小了。
还有一个技巧是 将方法替换为方法对象,在一个方法内部可以提取的代码块,会访问一些局部变量,如果提取后,会导致参数列表过长,将这些局部变量放到一个内部类,然后通过对这个类的方法的访问,达到实现原有方法的功能。
比如还是JPacman游戏里有个创建Board的方法
public Board CreateBoard(Square[,] grid)
{
//断言
//初始化Board
...
for(int x=0;x<width;x++)
{
for(int y=0;y<height;y++)
{
Square square = grid[x,y];
//可提取的方法块
...
}
}
return board;
}
第一次重构的提取方法为
private void SetLink(Square square, Direction dir, int x, int y, int width, int height, Square[,] grid)
{
...
}
这个方法参数就过多了,这里提到的参数过多,也是提高可维护性的10条原则之一的(保持代码单元的接口简单)的要求。
改成使用新的类的替代方案如下
internal class BoardCreator
{
private Square[,] grid;
private Board board;
private int width;
private int height;
//构造函数
...
internal Board Create()
{
...
//调用新的提取方法
SetLink(square, dir, x, y); //减少了3个参数
}
private void SetLink(Square square, Direction dir, int x, int y)
{
...
}
}
最后在原来的方法CreateBoard(Square[,] grid)里面就只需改成一行
return new BoardCreator(grid).Create();
这样改造后,每个方法行数不超过15行,而且局部变量和参数grid都变成了类BoardCreator的字段。
常见的反对意见:
代码单元过多会影响性能,调用次数的增加会影响性能,但是对于.net运行时来说,方法调用的耗时可以忽略不计,除非是在一个循环中执行了成百上千次的代码单元,这种情况很少很少,所以不要牺牲可维护性来优化性能,除非是有可靠的性能测试证明提高了维护性的代码存在了性能问题,而且你提出的优化性能措施真有效果。
代码分散开难以阅读,从心理学角度讲,并不成立,人的当前记忆大概只能记住7件事物,当阅读一个超过7行的代码单元时,就比较难记住所有内容了。
会助长不当的格式化,短小单元并不是将多个语句和括号放在一行,在规范的格式化代码下,15行正确格式化的代码能编写出有效的代码单元,这是这个原则的追求目标。
我的单元真的不能再拆分了,比如switch就不太好压缩代码行数,不过可以用一些设计模式来替代switch。
拆分代码单元没有带来明显的好处,像DoSomethingOne,DoSomethingTwo,DoSomethingThree这样的方法并不能带来什么好处,还不如将所有代码都放在一个长的方法DoSomething中,这是反对者的一个意见。每个更短的代码单元都比一个长的DoSomething方法更容易理解,方法名可以充当代码单元的说明文档,较长的DoSomething方法通常会包含多个任务,只有当需求完全一样的时候,才可以重用DoSomething方法。