论坛首页 Java企业应用论坛

代码重构之字符串与包的讨论

浏览 1589 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-03-08  
接着前两章的学生信息系统继续,学生信息系统的用户需要一个报表,列出课程的清单。
在CourseSession中增加两个变量页眉和页脚
private static String ROSTER_REPORT_HEADER = "页眉,学生信息报表";
private static String ROSTER_REPORT_FOOTER = "页脚,学生信息报表";

然后增加方法getRosterReport返回报表信息。
String getRosterReport(){
	StringBuilder buffer = new StringBuilder();
	buffer.append(ROSTER_REPORT_HEADER);
	Student student = students.get(0);
	buffer.append(student.getName());
	buffer.append('\n');
		
	student = students.get(1);
	buffer.append(student.getName());
	buffer.append('\n');
		
	buffer.append(ROSTER_REPORT_FOOTER+students.size()+'\n');
		
	return buffer.toString();
		
}
在很多地方使用了'\n'表示缓换行,这样做不仅有冗余,而且难以移植----不同平台使用不同的特殊字符序列表示换行。类java.lang.System中包含一个方法getProperty,此方法以一个系统属性的键值作为参数,并返回与该键值相关的系统属性。其中一个属性是line.separator。参考java API文档,在unix中该属性的值为'\n',然而在windows中,值为'\r\n'。应该使用getProperty(line.separator)弥合不同平台直接的差异。
在CourseSession类中定义一个换行常量
static final String NEWLINE = System.getProperty("line.separator");

然后代码相应变为
private static String ROSTER_REPORT_HEADER = "Student"+NEWLINE+"-----"+NEWLINE;
private static String ROSTER_REPORT_FOOTER = NEWLINE + "#Student";
String getRosterReport(){
	StringBuilder buffer = new StringBuilder();
	buffer.append(ROSTER_REPORT_HEADER);
	Student student = students.get(0);
	buffer.append(student.getName());
	buffer.append(NEWLINE );
		
	student = students.get(1);
	buffer.append(student.getName());
	buffer.append(NEWLINE);
		
	buffer.append(ROSTER_REPORT_FOOTER+students.size()+'\n');
		
	return buffer.toString();
		
}
单职责原则:
学生信息系统不断需要新的报表,可以预见,因为增加报表,需要不断地改变类CourseSession。面向对象有一个基本的设计原则:一个类只做好一件事,由于只做一件事,所以应该只有一个动机。这就是单职责原则
生成报表,如报名表,是改变类CourseSession 的另一个动机,违背了单职责原则。
创建一个新类RosterReporter 来生成报表。
public class RosterReporter {
	
	static final String NEWLINE = System.getProperty("line.separator"); //换行常量
	private static String ROSTER_REPORT_HEADER = "Student"+NEWLINE+"------"+NEWLINE;
	private static String ROSTER_REPORT_FOOTER = NEWLINE + "#Student";
	
	private CourseSession session;
	
	public RosterReporter(CourseSession session) {
		super();
		this.session = session;
	}
	
	String getRosterReport(){
		StringBuilder buffer = new StringBuilder();
		buffer.append(ROSTER_REPORT_HEADER);
		for(Student student:session.getAllStudents()){
			buffer.append(student.getName());
			buffer.append(NEWLINE);
		}
		
		buffer.append(ROSTER_REPORT_FOOTER+session.getAllStudents().size()+NEWLINE);
		
		return buffer.toString();
		
	}
	
	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();
	}

}
在CourseSession类和RosterReporter中都需要createDate工具方法。这个很明显是一个小重复,如果您不进行重构,您就打开了大规模的重复的大门,很快您的系统中就会出现代价高昂的重复。在一个大型系统中,或许会有很多创建日期的方法,每个方法都有类似的代码。

可以创建一个日期工具类 DateUtil来统一日期的创建和维护。
public class DateUtil {
	
	/**
	 * 
	 * @param year 年
	 * @param month 月份
	 * @param date  月中的天数
	 * @return 返回一个根据参数year,month,date组成的日期实例
	 */
	//可以写成静态方法。
	public static 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();
	}
}

包结构的一些约定
迄今为止,您编写的类都在包studentinfo包中,典型的组织包的方法是:分离用户接口类和表示业务逻辑的底层类。用户接口负责与最终用户的交互。

如下面的包结构是根据j2ee的mvc模式来划分的,cn.com.common包中放工具类:如日期生产类等,接口(cn.com.dao/cn.com.service)和实现类(cn.com.daoimpl/cn.com.serviceimpl)放在不同的包中,且包的分类都是根据模块来分类的。



类的访问权限
为了更安全的编程,推荐的顺序是:首先是最受限的访问,然后需要时打开相应的访问权限。暴露太多的类给客户,会导致客户对系统的细节产生不必要的依赖。如果您改变了某些细节,客户的代码就可能无法继续工作,而且,打开太多的访问权限,会使您的代码逐渐被破坏。


  • 大小: 28.3 KB
论坛首页 Java企业应用版

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