重构的方法

重构,其实很简单,它的目的就是让程序变得更容易被理解,更具有可维护性,结构更合理。重构应该是我们平时写代码过程中必不可少的一部分,比如给函数起了一个更好的名字、把大函数拆分成几个小函数等都属于重构。重构的经典书籍包括Martin Flower的《重构-改善既有代码的设计》、Joshua Kerievsky的《重构与模式》,本系列的所谓36计是我多年来使用最为频繁的重构策略和编码准则,有自己总结的,也有书上提到过的,希望对大家能有所帮助。

 第一计:参数列表对象化

  公有函数的参数应尽可能保持不变,因为很多地方都会调用它,修改参数后需要修改它的调用处,另外,它的参数列表不宜过长,数量尽量保持在5个以内,长参数列表会增加该函数的调用难度。对于参数较多或者参数经常变化的公有函数,较好的办法是引入参数对象,即该函数的参数只有一个,它就是参数对象,具体的参数都在该对象中声明,为函数引入参数对象有以下几个好处:

1、保持函数接口的不变性,修改函数参数只需修改参数对象中的成员变量。

2、调用方便,调用方不用再关心参数的顺序。

以下代码片段是一个添加用户函数声明:

 

public long insertUser(String name,int age,String email,String address,String phone,String birthDay)

 

每当添加或删除用户的字段后都要修改insertUser的参数列表,调用者也需要修改,而且参数较多时,不容易记忆。

以下是引入参数对象后的形式:

 

复制代码
public class UserParam{
  public String name;
  public int age;
  public String email;
  public String address;
  public String phone;
  public String birthDay;
}

public long insertUser(UserParam user);
复制代码

 

第二计:条件运算符赋值代替if else赋值

  对于根据条件为变量赋值的情况,可以有两种方式,一种是通过if-else:

 

int value;
if(condition)
  value = 1;
else
  value = 2;

 

  另一种是通过条件运算符:

 

int value = condition ? 1 : 2;

 

  第二种方式明显要比第一种方式好,但是很多人却钟爱第一种方式,可能是if-else习惯了。

 

第三计:节约使用系统资源

 即使在写代码时,我们也应该养成“节俭”的习惯,不要随便浪费系统提供的资源,对于那些较占用空间、影响性能的对象,应该直到真正要用的时候才创建或者初始化,因此在提供这些对象的函数实现中,尽量采用如下形式:

 

复制代码
// 管理数据库连接的类
public class DataBaseConnectionHolder{
  private Connection conn;

  public Connection getConnection(){
     if(conn == null){
       conn = new Connection();
       conn.init(); 
      }
     return conn;
   }

}
复制代码

 

另外,我们可以通过引入缓存机制(如对象池)来充分利用系统资源,可以参看这篇文章:GoF著作中未提到的设计模式(5):Object Pool

 第四计:为接口引入抽象版本

  在 声明一个新的接口时,不能保证该接口不会被修改,有可能会经常修改它,每一次修改接口都要修改相应的实现类,如果某个接口是公共库的一部分,那么修改接口 的代价是较大的,用到该接口的所有程序都需要重新修改、编译...,通过为接口引入抽象版本可以解决这个问题,

因为实现接口的话,你必须实现接口的所有方法,但是你用继承抽象类的话,是可以不用重写所有的方法的,所以必须要为接口引入抽象类

例如为下面的接口增加一个抽象类:

 

复制代码
public interface Widget{
  public void draw();
  public void layout();
public void invalidate(); public void show(); }
复制代码

 

复制代码
public abstract class AbstractWidget implements Widget{
  public abstract void draw();
  public void layout(){};
  public void invalidate(){};
  public void show(){};
}
复制代码

 

 这样Widget的实现类可以直接从AbstractWidget继承,如果要修改Widget接口,则只需要修改AbstractWidget即可,对于其他实现类没有影响。

 

第五计:消灭魔法数

  编程新手一般都会直接将表示类型或状态的数字直接写在处理逻辑中,代码的作者 能明白该数字所表示的含义,但其他人读到这段代码时就很有可能看不懂了。即使代码的作者再过一段时间来看这部分代码,也可能会忘记该数字的含义,而且,当 我们要修改魔法数的值时,过程是很繁琐的,很有可能会有所遗漏,所以,最好的办法是彻底消灭程序中的所有魔法数,通过常量定义、枚举等方式来避免魔法数的 出现。

 

第六计:使用断言、异常确保实现的正确性

  使用断言的目的是告知其他程序员代码中某处必须要遵守的规矩,它是debug 版本中的一种确保程序实现正确性的手段,在正式发布的版本中,断言是不起作用的。在java中,启用断言需要增加一个编译选项,不过可以通过抛出异常来达 到相同目的,使用异常比断言要危险,因为在程序的正式发布版本中会引起崩溃,不过有时候崩溃总比程序的诡异行为更好,例如:

  

复制代码
// 表示集合的类
public class Collection{
  // 添加元素到集合中
  public void addElement(Element e){};
 
  // 获取指定位置的元素
  public void getElement(int index){}; 
}

// 表示只读集合的类
public class ReadOnlyCollection extends Collection{
  // 添加元素到集合中
  public void addElement(Element e){
    throw new UnsupportedOperationException("只读集合,不允许添加元素");
  }
  
  // 获取指定位置的元素
  public void getElement(int index){};
}
复制代码

 调用ReadOnlyColletion派生类必须遵守规矩:不能调用addElement,否则抛出异常干掉程序!

posted @ 2012-11-08 17:03  天殇月痕  阅读(580)  评论(0编辑  收藏  举报