论坛首页 入门讨论版 企业应用

应领导要求,试写规范,请大家指点改进

浏览 984 次
该帖已经被评为新手帖
作者 正文
最后更新时间:2008-02-19
最近领导让我写一下规范东西(我想主要是给毕业不久的同事看)
我也是刚到公司不久,很多东西不熟
根据这几个月的工作情况写了一下
请大家指点改进后,我在提交给领导

这是她的要求
10.hibernate相关的类结构规范(包、类划分,命名要求)
20.hibernate的Spring配置规范(样例)
30.Spring的使用规范(配置要求【WEB、APP】、使用样例)
40.hibernate+Spring的整体样例代码
50.配置文件路径指定要求(配置文件在模块中的命名,在内存变量的命名)
60.log要求(log对象的声明、写log规则)
70.在Session、request中的变量命名要求

目前公司使用struts1 ,我少量引入spring hibernate dwr


1.包的结构
原则是包名要简短,类名和方法名要准确,一看名字就知道他的功能
方法名称同样,不在短而在准确。
方法长度不最好不要超过100行,凡是你感觉到一个方法里的一段内容需要写下注释说明一下,不要犹豫了,重构一下,提出来做个新的方法吧。

根结构不用说肯定是公司的名称加模块名称
如com.inspur.kpiengine.report (如果有重来的机会 我建议包kpiengine替换成kpi)

划分为 action  这里放mvc 控制器的一些类
domain 这里放的是领域模型对象
service 这里都是定义业务接口,也就是能提供哪些服务
service下面有impl包,这里是业务接口实现类

关于在hibernate的hbm文件,有人推荐放在domain处,也就是与相应的pojo对象平行,但是由于我们的开发需要考虑兼容多种数据库,所以我建议,在domain下面再加hbm.XXX  xxx代表数据库名称
util 工具包
            还有一个模块常量接口Constant

以报表为例 最后的大概样子
com.inspur.kpiengine.report
Constant.java
… 一些枚举类
com.inspur.kpiengine.report.action
com.inspur.kpiengine.report.domain
com.inspur.kpiengine.report.domain.hbm.ora
com.inspur.kpiengine.report.service
com.inspur.kpiengine.report.service.impl
com.inspur.kpiengine.report.util

如果使用struts 1 这个与spring不好结合的表现层框架我建议再加一个 factory 稍后再说
这里没有到层
com.inspur.kpiengine.report.dao
com.inspur.kpiengine.report.dao.impl
我觉得不加dao层,使用HibernateDaoSupport做dao基本上已经够用了,
我在做一个系统中在上面的基础上又加入了cache 层和dao层,开发和维护量都会增加不少,有点得不偿失。

如果使用spring mvc 建议加上form包,里面又form类,在复杂查询的时候用于收集、校验提交的表单比较方便,可以参考给的样例工程
   
最后更新时间:2008-02-19
2 .domain
这里面的类都要实现java.io.Serializable接口,并且生成serialVersionUID
根据业务逻辑重写hashCode,equals
这两个方法重写很简单,代码生成工具就能完成
另外toString 最好也能重写,其实也很简单 例如:
public String toString() {
		ToStringBuilder builder = new ToStringBuilder(this);
		builder.append("id", id).append("name", name);
		return builder.toString();
}
一些常量一定要定义
例如报表类型excel pdf rtf,不应该出现:在jsp里面直接就是一个字符串” excel” 在java里面也还是一个字符串”excel”
最好这些定义成枚举类型 enum ,
public enum ReportFileType {
	EXCEL,PDF,RTF
}
退而求其次 再Report 中定义3个字符串常量也是好的

另外领域模型对象中,方法不能仅仅就是getter and setter 一些逻辑最好也要放到这里面,而不要放到action中或者util里面
比如Emplee 对象 有个regTime属性表示用户注册的时间,如果我们要获得这个员工的工龄,直接在Emplee对象中提供getXXX方法直接获得工龄(当然了,这个方法的实现可能会用到XXXXUtil)

领域对象id 的类型,如果对象持久化到xml文件那么 String/Long都可以了(String可能会更好修改) 如果要是持久到db 一定是Long 不能是long int Integer 等,Integer的长度不够,已经出现例子了,int,long 的默认值是0 不容易代表没有的含义。

3.action
目前还没有确定到底使用spring 还是webwork,由于我对spring比较熟悉,就以spring说事了
首先我们的目前的系统结构没有清理session 的机制,所以传输对象,最好放到requestz中,必须放在session中的要遵循下面规则。Session的key值要在Constant 中定义常量,名称以SESSION 开头,+模块名称,+相应的名称,为将来清理session 做准备
如:
request.getSession().setAttribute(Constant.SESSION_REPORT_REPORT, report);


aciton中不能存在业务逻辑操作,他的工作就是收集数据,调用server 处理,再把数据显示到页面上,(在这之前可能会对数据进行处理,方便页面显示)

一个action的方法就要做一件事情,我见到有这样的程序,一个action 的方法即做了登陆(login)又做了注销(logout)逻辑混在一起,不容易阅读。为什么不能拆成两个public 方法login logout,如果两个方法中有重复的代码可以提出来做个private 方法呢
Login(){
….
share();
….
}

logout(){
….
share();
….
}

Share(){
….
}


在做表现层的时候有个工具类RequestUtil 能方便收集数据,请查看相关的文档。

4.service
首先要自己定义一个异常了,不要每次抛的异常都是Exception,并且什么信息都不输出
我觉得应该自己定义一个模块的ServiceException,可能各个模块的ServiceException可能共同继承同一个ServiceException,至于这个ServiceException 是受控的还是非受控的,我也没有好的主意,不过在struts1 还是受控的好 强制在action 中try catch ,(不过spring 的事务回滚好像没有了) 如果使用springmvc 或者struts2 还是非受控的好,在拦截器中搞定一切

Service 的接口名称 以Service结尾 如ReportService
Service接口的实现类 以Impl结尾如ReportServiceImpl
如果多种实现方式则加上实现的方式 如ReportServiceHbmImpl
剩下的分两部分来说
1)数据持久到xml文件
这是目前我们系统中普遍使用的,建议不要使用缓存,这样会存在个种问题,还是每次使用对象都,从xml文件读取生成比较好。

目前我们使用的是digester读,使用dom4j写 ,推荐使用xstream 对简单的xml文件做读写,能减少很多工作量,可以查看实例工程
2)数据持久到db
Service接口要继承CURDService 接口
Service接口实现类继承BaseServiceImpl 这个实现类

方法命名:
查找以find开头
删除以remove开头其他
save
update
saveOrUpdate
另外以find开头的方法,只能是查找不能有任何的增删改操作
具体相关的信息请查看springHibernateDAO.jar 的相关文档
   
0 请登录后投票
最后更新时间:2008-02-19
5.配置文件

关于由于配置文件出错抛的异常一定要抛出ConfFileException 这个异常,不要在抛Excepiton了
目前我们系统的配置文件很多,按照程序和配置文件分离的意思,这样设计的
有一个文件根路径,在initializer里用ETC_ROOT属性指定,下面会有一些文件夹,每一个文件夹代表一个模块的文件根目录,里面存放这个模块用到的数据文件,和配置文件。这样重新部署一个新的环境,仅仅改动initializer的ETC_ROOT 基本能满足要求了。
1) 如果文件是数据文件,最基本的我们会有3个文件,数据文件Data.xml ;digester 解析的rule.xml;监视data.xml文件的 monitor.properties;data.xml放在initializer指定的配置文件根目录的相应模块下面,rule.xml放在classpath下面和读取xml的serviceImpl类同目录,monitor.properties使用同一个( 在etc_root 下面)读取其中的不同属性即可,在serviceImpl的spring 配置文件中指定他的模块名称,rule.xml/ Data.xml文件名称  etc路径在initializer 的ETC_ROOT 指定,reportPublish是报表发布模块reportPublish.xml是他data文件

PublishServiceImpl是serviceImpl类,相应的rule文件publishDefRule.xml
下面的是spring配置文件
  	<bean id="publishService"
		class="com.inspur.kpiengine.report.publish.service.impl.PublishServiceImpl">
		<property name="confSystemPropName" value="ETC_ROOT" />
		<property name="etcAbstract" value="reportPublish" />
		<property name="reportPublishDefineConfFile" value="reportPublish.xml" />
		<property name="ruleFile" value="publishDefRule.xml" />

	</bean>
下面是使用digester 解析的一个例子

protected ThresholdRoot init() {
		InputStream ruleStream = getRuleInputStream();
		String confFullPath = getConfFileFullPath();
		if (log.isDebugEnabled()) {
			log.debug("预警配置文件:" + confFullPath);
		}
		KpiDigester kpiDigester = null;
		try {
			kpiDigester = KpiDigesterFactory.createDigester(ruleStream);
			ThresholdRoot root = (ThresholdRoot) kpiDigester.parse(confFullPath);
			return root;
		} catch (Exception e) {
			e.printStackTrace();
			log.error("解析预警出现问题,\n" + "预警rule file: " + ruleFile + "\tclasspath :" + getClass() + "\n" + "配置文件:" + confFullPath);
			throw new ConfFileException(etcAbstract, defFile, e);
		}
	}

	private String getConfFileFullPath() {
		ConfFilePathParam param = new ConfFilePathParam();
		param.setConfFileName(defFile);
		param.setConfRootOnSysProp(confSystemPropName);
		param.setModleConfRelationRoot(etcAbstract);
		return KpiConfigHelper.getConfFileRealPath(param);
	}

	private InputStream getRuleInputStream() {
		if (log.isDebugEnabled()) {
			log.debug("预警rule file: " + ruleFile + "\tclasspath :" + getClass());
		}
		return getClass().getResourceAsStream(ruleFile);
	}

2)如果是一般的properties 文件,就放在classpath 根下面就行了

6.log4j,
不论是java文件还是jsp 在提交到版本服务器后,不能存在System.out.print
Log4j在 java中定义
private Log log = LogFactory.getLog(getClass());
不要再private Log log = LogFactory.getLog(“zhibiao”);

配置文件,数据文件的路径信息一定要用log输出,
一些不重要的,自己开发时看的信息使用debug 输出 并且带判断
如:
if(log.isDebugEnabled()){
			log.debug("报表发布ReportPublish: " + reportPublish);
		}
(这个时候就能看出来toString的用处了)

在catch 块 不要把异常吃了
用log.error输出
catch (Exception e) {
			e.printStackTrace();
			log.error("解析 报表发布出现问题,\n"+"报表发布rule file: " + ruleFile + "\tclasspath :" + getClass()+"\n"+"配置文件:"+confFullPath);
			return null;
		}


总之,日志能够让人知道当前程序大概运行到哪里了,如果出了错误,能够知道错误出在哪里了。

7.注释
类的注释一定要有这个类的基本描述 ,能有些业务逻辑的描述最好
方法注释:
Getter 和setter 一般不需要,有特殊含义的除外
Private 和protected 可以没有
Public 的一定要有注释 对这个方法有进行描述,输出的结果也要描述一下
抛出的异常,这个也要写,如:配置文件出错会抛出ConfFileException:这个我们很容易在jdoc中忘记写
另外特殊的返回也要描述一下,如:不会返回null 出错返回空list
如果返回的是map 对key和value  也要有描述

8.集合类型一定要定义泛型 尤其是map

9.Jsp 绝对不能出现类似下面的东西
form.submit();
window.local.reload();

我们的jsp页面存在大量的java代码,希望能一起重视,

这些代码为什么不能移到java中
一个简单的例子:页面要显示查询出来的一组数据list,这个list的某个字段在页面上显示需要进行一下复杂的转化,,现在的做法是在jsp中for循环输出list内容,到了这个要转化的字段在转一下。那么为什么不能在action中就把这步转化做了,到jsp中使用一下tag直接输出呢。
另外一些准备工作不能在 action中做,如生成一个下拉菜单,checkbox组,为什么不在action中把数据(连同回显的数据)准备好,在jsp中使用tag直接输出,而偏偏要在jsp中使用for 和if 来做这些事情


另外还存在人人都重复发明工具轮子的问题,希望有人能尽快的把一些工具类的使用说明整理出来,大家都阅读一下

10.spring bean id定义 jsp name 定义
都类似java的变量定义方式如何,即首字母小写,其他单词首字母大写的驼峰是风格
这样在spring autoware byname 和mvc跳转到jsp的时候能有点方便。
(这条可要可不要)
   
0 请登录后投票
最后更新时间:2008-02-22
读完让我了解了一些关于企业开发的事。对我来说,这些是必不可小的经验。谢谢分享。希望能看到更多的
   
0 请登录后投票
最后更新时间:2008-02-22
lzmhehe 写道

Log4j在 java中定义
private Log log = LogFactory.getLog(getClass());
不要再private Log log = LogFactory.getLog(“zhibiao”);

这儿可以使用1个工具类类避免每个类都声明:
private Log log = LogFactory.getLog(getClass());
代码片段:
public class log {
/**
 * 获取方法调用者类的日志记录器
 */
public static Logger getLogger() {
	Class<?> caller = null;
	// 找到第一个除本类以外的调用者
	// 0=class sun.reflect.Reflection
	// 1=class 本类
	for (int i = 2; log.class.equals(caller = Reflection.getCallerClass(i)); i++)
		;
	// String caller = new Throwable().getStackTrace()[2].getClassName();
	return Logger.getLogger(caller);
}

public static void debug(Object msg) {
	getLogger().debug(msg);
}

public static void debug(Object msg, Throwable t) {
	getLogger().debug(msg, t);
}
//其他方法略。。。
}


lzmhehe 写道

配置文件,数据文件的路径信息一定要用log输出,
一些不重要的,自己开发时看的信息使用debug 输出 并且带判断
如:
if(log.isDebugEnabled()){
			log.debug("报表发布ReportPublish: " + reportPublish);
		}
(这个时候就能看出来toString的用处了)


没有必要再用log.isDebugEnabled()进行重复判断。
log4j的debug()方法自己会判断:
    if(repository.isDisabled(Level.DEBUG_INT))
      return;
   
0 请登录后投票
最后更新时间:2008-02-22
以前写过,似乎写几百页的doc文档没有一个简单的ppt来得实在
   
0 请登录后投票
最后更新时间:2008-02-23
建议楼主重视备注,强性要求每个方法和类都写备注,类的版本号,编写人都写上
   
0 请登录后投票
最后更新时间:2008-05-16
sswh 写道
lzmhehe 写道

Log4j在 java中定义
private Log log = LogFactory.getLog(getClass());
不要再private Log log = LogFactory.getLog(“zhibiao”);

这儿可以使用1个工具类类避免每个类都声明:
private Log log = LogFactory.getLog(getClass());
代码片段:
public class log {
/**
 * 获取方法调用者类的日志记录器
 */
public static Logger getLogger() {
	Class<?> caller = null;
	// 找到第一个除本类以外的调用者
	// 0=class sun.reflect.Reflection
	// 1=class 本类
	for (int i = 2; log.class.equals(caller = Reflection.getCallerClass(i)); i++)
		;
	// String caller = new Throwable().getStackTrace()[2].getClassName();
	return Logger.getLogger(caller);
}

public static void debug(Object msg) {
	getLogger().debug(msg);
}

public static void debug(Object msg, Throwable t) {
	getLogger().debug(msg, t);
}
//其他方法略。。。
}


lzmhehe 写道

配置文件,数据文件的路径信息一定要用log输出,
一些不重要的,自己开发时看的信息使用debug 输出 并且带判断
如:
if(log.isDebugEnabled()){
			log.debug("报表发布ReportPublish: " + reportPublish);
		}
(这个时候就能看出来toString的用处了)


没有必要再用log.isDebugEnabled()进行重复判断。
log4j的debug()方法自己会判断:
    if(repository.isDisabled(Level.DEBUG_INT))
      return;




isDebugEnabled 的开销 小于 debug

生产环境下面debug的不要执行的
如果没有isDebugEnabled
debug 可能会很占用资源
   
0 请登录后投票
论坛首页 入门讨论版 企业应用

跳转论坛:
JavaEye推荐