论坛首页 综合技术论坛

代码重构之第二章 从基础开始

浏览 1321 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-03-06   最后修改:2010-03-06
第二章 从基础开始
上一章定义了一个Student类。在学校里,学生都有课程安排,所以需要一个CourseSession类表示课程安排,它存储上课时间和教师信息,同时保存一份这门课程的学生。
  
class CourseSession{
	private String department; //课程名称
	private String number;  //编号
	
CourseSession(String department,String number){
	this.department = department;
	this.number = number;
	}
}


CourseSession 需要存储学生,所以需要一个集合来存储学习,大家立刻会想到用ArrayList
private ArrayList<Student> students = new ArrayList<Student>();
注意用import包把java.util.ArrayList包含进来,包提供了一种将相关的类进行分组的机制。
包有几个用途:
首先,将一组类打成包,给开发者提供了相当的便利,避免开发者必须同一时刻在成十,成百,甚至上千的类中查找。
第二,类被打成包也有发布地目的,可以方便地重用模块或者子系统。
第三,包在java中提供了命名空间。包提供了赋予类一个唯一名字的机制,从而最小化类命名冲突。比如您的类有全称 com.mycompany.studentinfosystem.Student ,第三方API也许会用 com.thirdpartyco.expensivepackages.Student.
习惯:默认的包对于例子或者非商业程序是允许的。但是,对于任何真正的软件开发,所有的类都应该某些包而不是默认包。事实上如果您把类放在        默认包,那么您不可能从其他包使用这些类。
      您地开发团队应该就包的命名达成一致。多数公司把他们的域名倒过来,作为包名的开始。例如,一个名叫Minderbinder Enterprise的公司或许用 com.minderbinder作为包名的开始
声明类参数化类型的一个好处是:限制Java.util.ArrayList只能包含Student对象,从而避免不小心把其他类型的对象添加进去。
重构代码
隐藏CourseSession不必要暴露的细节。您封装了student集合,只可以用get、add、size方法来处理该集合。
例如
    
   // 暴露了CourseSession的细节
ArrayList<Student> getAllStudents(){
    return students;
}
   //用get方法根据索引得到student
Student get(int index){
    return students.get(index);
}

封装提供了两个优点:首先,目前在ArrayList中保存student列表。ArrayList是有着特定用法和性能指标地一种数据结构。如果您将该列表直接暴露给客户代码,客户代码将依赖于用ArrayList存储student的事实。这样依赖意味着您无法轻易地改变students的存储形式。第二,暴露完整的集合意味着其他类可以操作该集合——加入新的student对象,删除student对象,诸如此类——而且CourseSession类无法意识到这些改变。CourseSession对象的完整性将遭到破坏。

关键点:
    1. 用关键子staticfinal来声明类常量,类常量是成员变量。提醒一下,关键子final表明该成员变量的引用不能改变,即指向不同的值。关键字static意味着在没有创建类的实例的情况下就可以使用该成员变量。
    例子:
    
class ChessBoard{
	static final int SQUARES_PER_SIDE = 9;

}

   使用方法:int numberOfSquares = ChessBoard.SQUARES_PER_SIDE;
   按照约定,用大写字母定义类常量。当所有的字母都是大写,用“驼峰模式”来标记就不可能,所以标准方法是采用下划线来分割单词。
**********************************************
课程安排需要开始时间和结束时间。用来标记第一天和最后一天,课程一般都是16周(15周课程,在第7周后会用一周的休息时间)
  在CourseSession类中定义一个变量startDate;
  private Date startDate;  //课程安排需要开始时间
  然后重载(不是修改原来的构造函数,而是多写一个构造函数)构造函数
 
CourseSession(String department,String number,Date startDate){
	this.department = department;
	this.number = number;
	this.startDate = startDate;
}
Date getEndDate(){		
	GregorianCalendar calendar = new GregorianCalendar();
	calendar.setTime(startDate);
	int numberOfDays = 16*7-3;
	calendar.add(Calendar.DAY_OF_YEAR, numberOfDays);
	Date endDate = calendar.getTime();
	return endDate;
}

增加对工厂方法的理解
  如果方法足够短,我们就容易提供有意义的、简短的名字命名方法。如果发现为方法命名很困难,请考虑将其拆为几个更小的方法,每个方法只做一个简单的、可以命名的事件。
 
 int year  = 103;
   int month = 0;
   int date = 6;
   Date startDate = new Date(year,month,date);

   像上面的代码让人看着糊涂——因为要相对1900年份来指定年份,月份必须指定在0到11,而不是1到12直接。可以通过增加一个生产Date实例的方法来创建时间
 
Date createDate(int year,int month,int date){
     return new Date(year-1900, month-1, date);
 }
  用这个方法好比一个工厂生产时间实例,给我年月日,就给你个日期实例,相当简单。这样创建date实例有助于封装对局部变量的理解。

消除所有的警告。
  忽略编译警告就像牙虫的危害---迟早您会为其付出代价,而且为您长期的忽视付出昂贵的代价。
  用Calender创建日前而不是用Date创建日期。消除警告(想必大多数人用Date都有警告)。
  Date createDate(int year,int month,int date){
  	GregorianCalendar calendar = new GregorianCalendar();
   	calendar.clear();
	calendar.set(Calendar.YEAR, year-1900);
	calendar.set(Calendar.MONTH, month-1);
	calendar.set(Calendar.DAY_OF_MONTH, date);
	return calendar.getTime();
}
   


int numberOfDays = 16*7-3; 

这样的地方容易让人难以理解。可以通过增加注释的方法帮助理解,
例如 weeks * days per week -3days,
但是错误或容易引起误解的注释是声名狼藉的。上面的注释是无效注释的经典例子
一个好的解决方案是:
final int sessionLength = 16 ;
final int daysInWeek = 7;
final int daysFromFridayToMonday = 3;
int numberOfday = sessionLength * daysInWeek - daysFromFridayToMonday;

这样更富有表现力










论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics