软件开发-重构

发表于:2014-05-26来源:博客园作者:cmmi点击数: 标签:重构
重构是对软件内部结构的一种调整,目的是在不改变软件之可察性前提下,提高其可理解性,降低其修改成本。关于重构的至理明言如下: 任何一个傻瓜都能写出计算器可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员;

  重构是对软件内部结构的一种调整,目的是在不改变软件之可察性前提下,提高其可理解性,降低其修改成本。关于重构的至理明言如下:

  任何一个傻瓜都能写出计算器可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员;

  事不过三,三则重构;

  当你接获bug提报,请先撰写一个单元测试来揭发这个bug;

  当你感觉需要撰写注释,请先尝试重构,试着让所有的注释变得多余;

  当你发现自己需要为程序增加一个特性,而代码结构使你无法方便的这样做,就先重构那个程序;

  重构之前,必须建立一套可靠的测试机制;

  写软件就像种树,优秀的程序员挖成小坑后随及填好,继续挖下一个,只会产生一系列小坑,不会有大坑,菜鸟则不会意识到所挖的坑正在变大,还是不停的挖,直到自己掉进大坑,爬不出来,陷入无尽的痛苦深渊;

  开发时间越长,越能体会垃圾代码的痛苦,却不知道如何改进;

  Kent Beck:我不是一个伟大的程序员,我只是个有着一些优秀习惯的好程序员而已;

  变量(Variable)

  不要定义一个临时变量多次重复使用,临时变量定义仍然应该可以自解释,从变量名称能够很好的理解变量的含义和作用。在定义一个临时变量后需要有一段业务逻辑才能够完成对临时变量的赋值的时候,可以考虑将这段逻辑抽取到一个独立的方法。

  doublegetPrice(){

  int basePrice = _quantity* _itemPrice;

  double discountFactor;

  if (basePrice > 1000) discountFactor = 0.95;

  else discountFactor = 0.98;

  return basePrice * discountFactor;

  }

  重构为:

  double getPrice(){

  return basePrice()* discountFactor();

  }

  private int basePrice(){

  return _quantity* _itemPrice;

  }

  private double discountFactor(){

  if (basePrice()> 1000) return0.95;

  else return 0.98;

  }

  当遇到复杂的表达式的时候,需要引入解释变量,因为复杂的表达式很难进行自解释。

  if ((platform.toUpperCase().indexOf("MAC")> -1)&&

  (browser.toUpperCase().indexOf("IE")> -1)&&

  wasInitialized()&& resize> 0 )

  {

  // do something

  }

  重构为:

  final booleanisMacOs = platform.toUpperCase().indexOf("MAC")>-1;

  final boolean isIEBrowser =browser.toUpperCase().indexOf("IE") > -1;

  final booleanwasResized = resize >0;

  if (isMacOs&& isIEBrowser&& wasInitialized()&& wasResized){

  // do something

  }

  减少对全局变量的使用,第一个是全局变量的生命周期很难控制,资源本身无法得到很快的释放,其二是过多使用全局变量导致在调用方法的时候很难完全清楚方法说需要的入口数据信息,其三,多处都可以对全局变量赋值,我们很难立刻定位到当前全局变量的值来源自哪里?

  分解方法(Extract Method)

  一个较大的方法往往也会分为多个小的段落,step1,step2,step3,在每一个步骤都会考虑添加注释说明。而这些相对较为独立的步骤就可以分解为不同的方法,在分解后方法名可以自解释方法的功能而不再需要额外的注释。在一个类里面如果方法里面有一段代码在多个方法中重复出现,需要抽取该类的公用方法。在多个不同的类中有一段代码重复出现,需要考虑将公用代码放到公用类中形成公用方法。

  方法名需要很好的自解释方法的功能,方法的返回尽量单一,方法的入口参数太多的时候应该考虑使用集合,结构或数据对象进行参数的传递。参数的传递可能出传递的是引用,但不要去修改入口参数的值。

  不要因为一个方法里面只有一行,两行很短而不考虑去分解,分解的时候更多的是考虑代码的自解释性。代码本身不是解释的技术实现机制,而是解释的业务规则和需求。如果代码不是解释的业务规则和需求,那么其它人员就很难快速理解。

  引入方法对象来取代方法,当发现一个方法只用到该类里面的几个关键属性,方法和类里面其它的方法交互很少,输出单一。由于该方法和这几个属性内聚性很强而和该类其它部分松耦合,因此可以考虑将方法和这部分属性移出形成一个单独的方法对象。

  移动方法,类的职责要单一,一个类的方法更多用到了别的类的属性,这个方法可能更适合定义在那个类中。

  class Account...

  private AccountType_type;

  private int_daysOverdrawn;

  double overdraftCharge(){

  if (_type.isPremium()){

  double result = 10;

  if (_daysOverdrawn > 7) result += (_daysOverdrawn -7)* 0.85;

  return result;

  }

  else return _daysOverdrawn * 1.75;

  }

  double bankCharge(){

  double result = 4.5;

  if (_daysOverdrawn > 0) result +=overdraftCharge();

  return result;

  }

  重构为:

  classAccount...

  private AccountType _type;

  private int _daysOverdrawn;

  double overdraftCharge(){

  return _type.overdraftCharge(_daysOverdrawn);

  }

  double bankCharge(){

  double result = 4.5;

  if (_daysOverdrawn > 0)

  result += _type.overdraftCharge(_daysOverdrawn);

原文转自:http://kb.cnblogs.com/page/68471/