NEWS 新闻资讯
首页 > 技术分享 > 新闻详情

系统重构注意事项36条之1-8

文章来源:本站原创作者:Deepfisher发布时间:2012年4月29日浏览次数:1006

  系统重构,这是几乎所有开发人员都会遇到的问题。它的主要目的就是让程序变得更容易理解,更具有可维护性,结构更合理。重构应该是我们平时写代码过程中必不可少的一部分,比如给函数起一个更好的名字、把大函数拆分成几个小函数等都属于重构。重构的经典书籍包括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,否则抛出异常干掉程序!

第七:串联函数调用

当一个类的大部分函数被较为频繁地调用,并且包含连续性地调用,那么可以考虑为这个类中那些没有返回值的函数增加返回值,即返回对象本身,这样就可以串联函数调用,使用起来较为方便,举个例子:

		// 表示用户的类
public class User{
  public void setName(String name);
  public void setAge(int age);
  public void setPhoneNumber(int phoneNumber);
}

下面是不使用串联函数调用的情况:

		User user = new User();
user.setName("West_Link");
user.setAge(3);
user.setPhoneNumber(122333);

 

下面是使用串联函数调用的情况:

		User user = new User().setName("West_Link").setAge(3).setPhoneNumber(123333);

只需要为那些函数增加一个User对象的返回值即可,如下:

		public User setName(String name){
   this.name = name;
   return this;
}

第八:临时变量在用到时才声明

 很多人喜欢在函数的开头把所有要用到的临时变量都声明了,我认为这种方式有以下几个缺点:

1、不利于代码的阅读,需要经常在变量的使用处和变量的声明处跳转,不方便。
2、容易造成资源的浪费,因为有些对象的初始化是比较耗费资源的,而函数可能在用到该对象之前返回。
3、不利于函数的拆分。
所以,我们应该尽可能降低临时变量的作用域,那样它能“捣乱“的范围就被缩至最小了。

本文由北京网站建设公司新网众志(http://www.xwzznet.com)编辑发布,如需转载请注明出处。
收缩
  • 电话咨询

  • 010-50975192