刚入行,接触的项目不多,第一次接触的一个SSH项目是这样分层的
抽象一个Dao接口,这个接口包含对象基本增删改查,然后提供一个Dao实现(比如叫Support),至此Dao层结束,然后业务层接口继承Dao接口,业务层继承Support。然后每一个模块一个Action.这样做一个好处就是直接通用一个Dao,不用每个业务类都写一个Dao。但坏处就是因为涉及到泛型,每个个数据表都要对应一个业务对象。如果数据库中有很多的表,就会有很多业务接口业务实现。
具体结构看贴图:
另一个SSH项目是这样分的:
每个模块各一个增删改查Dao加Dao实现,Dao之前互相交错。
问题:个人觉得这两种设计都不好,那么该如何设计,还有,这两个项目中的业务层代码跟Dao层代码感觉都差不多,比如有一个业务层的代码是这样的:
public void deleteIp(Filterip ip){
deleteDao.deleteIp(ip);
}
public void deleteDev(Devinfo devinfo){
deleteDao.deleteDev(devinfo);
}
public void deleteBackup(Backup bp){
deleteDao.deleteBackup(bp);
}
public void deleteDevdetail(Devdetail devdetail){
deleteDao.deleteDevdetail(devdetail);
}
那这样分逻辑层有什么意义呢,然后最繁重的是action,判断都写在Action方法里面,导致一个方法少则一百多行,多则几百行,一个类上千行。
[code="java"]
public String importFile() {
// TODO Auto-generated method stub
this.flag="import";
//从此处开始属于预先校验部分。
//这部分校验的全部放到一个StrutsUtils这样的工具类的validateFile方法中。统一返回String,如果为null,则验证成功,不为null,则验证失败,msg为失败信息
if (myFile == null) {
this.msg="导入文件名获取失败!";
return SUCCESS;
}
if (fileName == null || "".equals(fileName)) {
this.msg="导入文件名获取失败!";
return SUCCESS;
}
String[] tempfn=fileName.split("_");
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
boolean isImport=true;
try {
sdf.parse(tempfn[0]);
} catch (ParseException e) {
// TODO Auto-generated catch block
isImport=false;
}
if(!isImport){
this.msg="文件名不符合命名规则,请选择正确的备份文件导入!";
return SUCCESS;
}
//从此处结束属于预先校验部分
//下面文件处理部分,放到文件处理帮助类中,也可放到StrutsUtils工具类中。
String localfilePath = ServletActionContext.getServletContext()
.getRealPath("/backup/files");
File imageFile = new File(localfilePath);
if (!imageFile.exists()) {
imageFile.mkdirs();
}
localfilePath += "/" + fileName;
imageFile = new File(localfilePath);
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
Object keyname = session.getAttribute("keyname");
Object keyright = session.getAttribute("keyright");
int right = 0;
String pcname="";
if (keyright != null) {
right=Integer.valueOf(keyright.toString());
}
if(keyname!=null){
pcname=keyname.toString();
}
//这里需要业务层处理,直接在action调用
Backup bk=auditlogService.findBackupByName(fileName);
if (!imageFile.exists()) {// 如果文件存在不覆盖,直接返回导入成功
boolean isCopy=auditlogService.importFile(pcname, request.getRemoteAddr(), right, pcname, myFile, imageFile, request.getLocalAddr());
if(!isCopy){
this.msg="导入失败!";
}else{
this.msg="导入完成!";
}
}else{
if(bk==null){//如果文件存在,数据库没有备份记录
//下面若干语句应该放在业务层处理
Backup bkis = new Backup();
bkis.setName(fileName);
bkis.setStatus("0");
bkis.setAuthority(right);
String createtime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date());
bkis.setCreatedate(createtime);
auditlogService.backup(bkis);
}
Auditlog a = new Auditlog();
a.setSn(sn);
a.setInternettype(0);
a.setTypeid(right);
Auditlogtype auditlogtype = auditlogService.findTypeById(right);
if (auditlogtype != null) {
a.setTypename(auditlogtype.getName());
}
a.setCreatedate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
a.setAddress(request.getRemoteAddr());
a.setSubname(pcname);
a.setObjname(request.getLocalAddr());
a.setActionid(9);
a.setActionname("导入");
a.setLevelid(1);
a.setLevelname("一般");
a.setExpandrecord("导入文件:" + fileName+" 文件已存在,不能重复导入!");
a.setResult(1);
auditlogService.save(a);
this.msg="文件存在,不能重复导入!";
}
return SUCCESS;
}
[/code]
我之前做过类似的这种分层封装,我是这么做的,仅供你参考:
dao接口,如:
BaseDao接口(该接口提供常用的orm操作:save、update、delete、find、findByPage、searchBySQL……)
dao的实现类,如:
BaseDaoHibernate
BaseDaoIbatis
BaseDaoJdbc
这几种实现类各自完成orm框架需要做的常用操作。
service接口,实际上与BaseDao接口差不多,也是常用的增删改查,如:
BaseManager
service实现类,该实现类中注入BaseDao实例变量,完成常用的操作。如:
BaseManagerImpl
每个业务模块均定义自己的业务层、orm层接口,实现类。如:
UserManager extends BaseManager
UserManagerImpl extends BaseManagerImpl implements UserManager
UserDao extends BaseDao
这里以hibernate为例,
UserDaoHibernate extends BaseDaoHibernate implements UserDao
并把UserDao注入到UserManager中。
如果业务逻辑复杂,在业务层接口增加业务层方法,由实现类具体实现。
Action只要注入业务层bean,在具体方法中,做一些简单的预处理,再交给业务层处理业务逻辑
建议你看看springside的源码以及一些其它优秀的基础框架。对你肯定有帮助。只要基础框架封装好了,业务模块做的事情非常少,增删改查这样的简单模块,基本上UserDao、UserManager接口不需要任何其它代码。
当你对基础框架有深入的了解了,再建议你学习领域驱动等系统设计知识……
早上没事,帮你重构下,
你的逻辑是这样的
1、校验是否可导入
1.1、如果失败 直接返回
2、得到图片文件
3、如果文件存在 直接导入成功
3.1、如果文件无备份 备份
4、导入
4.1、导入成功/失败
5、记录审核日志
因此我给你重构第一段 你可以按照相应的路子继续重构
[code="java"]
public String importFile() {
//不使用异常
String errorMsg = canImportFile(); //此处把校验放到里边
if(!StringUtils.isEmpty(errorMsg)) {
this.msg = errorMsg;
return SUCCESS;
}
//或者使用异常
try {
assertCanImportFile();
} catch(ImportException e) {
this.msg = e.getMessage();
return SUCCESS;
}
}[/code]
还有比如
fileName == null || "".equals(fileName)
完全可以使用如apache commons lang包 缩短 如StringUtils.isEmpty(errorMsg)
建议学学apache commons 包
还有如下代码
[code]Auditlog a = new Auditlog();
a.setSn(sn);
a.setInternettype(0);
a.setTypeid(right);
Auditlogtype auditlogtype = auditlogService.findTypeById(right);
if (auditlogtype != null) {
a.setTypename(auditlogtype.getName());
}
a.setCreatedate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
a.setAddress(request.getRemoteAddr());
a.setSubname(pcname);
a.setObjname(request.getLocalAddr());
a.setActionid(9);
a.setActionname("导入");
a.setLevelid(1);
a.setLevelname("一般");
a.setExpandrecord("导入文件:" + fileName+" 文件已存在,不能重复导入!");
a.setResult(1);
auditlogService.save(a); [/code]
可以分为两步
log = populateAuditlog();
auditlogService.save(a);
比如备份也是这样
Backup bkis = new Backup();
bkis.setName(fileName);
bkis.setStatus("0");
bkis.setAuthority(right);
String createtime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date());
bkis.setCreatedate(createtime);
auditlogService.backup(bkis);
把这些代码抽象到一个方法中
这样你的主逻辑非常清晰
还有你这里边的功能应该放到服务层(如果事务加在服务层) 否则假设备份成功了 但往下执行出问题了 则回滚不了
在action 我们只需要组装我们的数据 然后把数据交给业务层/服务层处理 然后根据返回值/异常决定返回的页面 即action应该代码越少越好。。
1、分层,action、service、dao,然后每层中各模块代码放在一起
2、分模块,user、order,然后每模块中各曾代码放一起
3、分层,各层有再分模块
4、分模块,各模块中再分层
权衡利弊,根据需要选择