编写可维护软件的不朽代码随想-2

编写短小的代码单元

代码单元的长度应<=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方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值