论坛首页 Java企业应用论坛

为新的小型项目设计了一个架构,请批评指正

浏览 24780 次
该帖已经被评为良好帖
作者 正文
   发表时间:2006-10-12  
我想我们还是得把代码贴上来,
0 请登录后投票
   发表时间:2006-10-12  
Uranus 写道
我和楼主是一起做项目的,其实我们构建出来的系统架构没有解决的问题是怎么让接口发挥他的利用,如果用factory模式的话,就要在xml中加入相关的类,相当麻烦,不知道各位都是怎么解决这个问题的,


这个要针对具体业务吧,还得不断重构,项目只能在重构过程中完成进化,一蹴而就的接口设计恐怕难以做到.

我有一点看法不知大家是否赞同,那就是 架构设计以统一接口为基础 和项目的实际发展是冲突的.

以Domain层的类为例,不经过深入对领域的研究,谁能一口咬定两个类是否该实现统一接口呢,同样也不能肯定它们不能实现同一接口.一切设想要进过实践才见分晓,也就是说接口设计要等代码完成大部分才知道其优劣,好在我们还有重构的手段.

接口设计的优劣取决于程序员对项目知识的熟悉程度,对于熟悉的领域,比如API层面,程序员很熟悉,就容易写出优秀的代码,一到业务层面,千奇百怪的东西来了,代码就很容易乱.所以说,必须熟悉某个领域才能成为优秀的架构师,优秀的架构师脱离不开自己钻研的领域,所以成为架构师首先要成为领域专家.

领域分析实际是完整的项目中最难的一关,我们投入最多的精力也值得,这个做好了,项目就会"提领而顿,百毛皆顺".

有点乱,大家凑活看吧.^_^.
0 请登录后投票
   发表时间:2006-10-13  
修改了一下
0 请登录后投票
   发表时间:2006-10-13  
行为艺术家 写道
Uranus 写道
我和楼主是一起做项目的,其实我们构建出来的系统架构没有解决的问题是怎么让接口发挥他的利用,如果用factory模式的话,就要在xml中加入相关的类,相当麻烦,不知道各位都是怎么解决这个问题的,


这个要针对具体业务吧,还得不断重构,项目只能在重构过程中完成进化,一蹴而就的接口设计恐怕难以做到.

我有一点看法不知大家是否赞同,那就是 架构设计以统一接口为基础 和项目的实际发展是冲突的.

以Domain层的类为例,不经过深入对领域的研究,谁能一口咬定两个类是否该实现统一接口呢,同样也不能肯定它们不能实现同一接口.一切设想要进过实践才见分晓,也就是说接口设计要等代码完成大部分才知道其优劣,好在我们还有重构的手段.

接口设计的优劣取决于程序员对项目知识的熟悉程度,对于熟悉的领域,比如API层面,程序员很熟悉,就容易写出优秀的代码,一到业务层面,千奇百怪的东西来了,代码就很容易乱.所以说,必须熟悉某个领域才能成为优秀的架构师,优秀的架构师脱离不开自己钻研的领域,所以成为架构师首先要成为领域专家.

领域分析实际是完整的项目中最难的一关,我们投入最多的精力也值得,这个做好了,项目就会"提领而顿,百毛皆顺".
有点乱,大家凑活看吧.^_^.

这些我很赞同,也是我们现在缺乏的
没有对项目的深入理解是很难有一个合理的设计,尤其在接口的确定上,重构是必经之路
0 请登录后投票
   发表时间:2006-10-13  
其实拿到论坛上探讨也是一种办法,有时候真理越辨越明的. 讨论者还可以相互学习.
0 请登录后投票
   发表时间:2006-10-13  
行为艺术家 写道
dao就是简单数据访问,我觉得是没有必要做一个类层次的.我感觉它的权责过轻了,实际使用时为一时方便又容易分派给它很多额外任务,所以有必要分开成两层Domain和Service.

我认为系统中各部分传递的数据都应该是Domain(也就是你说的model)中各类及其集合和组合,logic是对数据从内(java)向外(view)或从外向内的处理,service搭起流动对象与持久层之间的桥梁.我感觉这样比较清晰.

请问cjmm 怎么看?(PS:这段是后来改的,原文是楼上怎么看,回完贴才发现楼上换人了,Robbin是否觉得那个小CheckBox有些不妥啊)


加一个DAO层主要是从TDD的角度出发,对业务层进行测试时可以通过mock独立于数据库进行。
我model里面放的是没有业务逻辑只有getter和setter的POJO,在DAO层save/update/get得到的是model对象。
这样子的考虑事实上是把模型与逻辑完全分离。
service里面会对model进行进一步封装,model不是提供给web的对象。

举个例子,要对用户进行管理,

package model;

public class User
{
    private String name;
    private String password;
    private long lastLoginTime;

   //以下是getter,setter和hashcode,toString,equals方法

}

package dao;
public interface UserDAO
{
  model.User getUser(String name);
  List getUsers();
  void saveOrUpdateUser(model.User user);
  void removeUser(model.User user);
}


package service.user;
public interface User
{
   String getName();
   String getPassword();
   boolean validatePassword(String pwd);
   void update();
}
public interface UserService
{
    User getUser(String name);
    User[] getUsers();
    void createUser(String name, String pwd);
    void removeUser(User user);
}

package service.user.impl
final class UserAdapter implements UserAdapter
{
  ...
}

final class UserService implements UserService
{
  ...
}

事实上在web层全部是通过接口得到对象,而在service层得实现中把model对象用adapter封装成适用于实际应用的对象提供给调用者,最大限度地在层次间进行解耦

0 请登录后投票
   发表时间:2006-10-13  
文章发表后居然会自动改变格式:(
0 请登录后投票
   发表时间:2006-10-13  
看起来还是要把代码弄上来,让大家帮我看看这几个问题

附件是我们设计的架构中注册用户的一个时序图,先检查用户名是否存在,不存在则将用户信息写入数据库,否则提示错误


代码的执行从register.jsp开始,用户输入用户名密码后提交,进入servlet:UserRegisterController
UserRegisterController的dePost方法
                String userName = request.getParameter("txtUserName");// 用户名
		String userPwd = request.getParameter("txtUserPwd");// 密码
		String target = null;

		User user = new User(userName, userPwd);// 构造领域对象

		UserRegister reg = new UserRegisterImpl();
		// 通过UserRegister接口,调用addUser方法添加用户
		if (reg.addUser(user)) {
			request.setAttribute("userName", userName);
			request.setAttribute("userPwd", userPwd);
			target = "/success.jsp";
		} else {
			target = "/failed.jsp";
		}

		request.getRequestDispatcher(target).forward(request, response);


问题一在这个servlet中,接收用户输入并以领域对象的形式来传递,接收和传递数据的方式我总觉得很笨拙,13楼yfmine  在帖子里提到的方法我没怎么看懂,还请明示。这里的逻辑很简单,成功和失败分别转向不同的页面。UserRegister 仅仅是一个接口,UserRegisterImpl怎是对它的具体实现。UserRegister 只有一个public boolean addUser(User user)方法,用于新增用户

UserRegisterImpl
/**
	 * 如果用户名可用则添加用户
	 * 
	 * @param user
	 * @return
	 */
	public boolean addUser(User user) {

		UserDao dao = new jdbcUserDaoImpl();
		// 通过UserDao接口调用checkUserAvailability和insertUser方法
		if (checkUserAvailability(user.getUserName()) && dao.insertUser(user)) {
			// 用户名可用且插入数据成功
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 通过getUserByUserName方法返回的引用判断用户名的可用性
	 * 
	 * @param userName
	 * @return
	 */
	private boolean checkUserAvailability(String userName) {

		UserDao dao = new jdbcUserDaoImpl();
		User user = dao.getUserByUserName(userName);

		if (user == null)// 未返回引用,即不存在同名用户
			return true;
		else
			return false;
	}


在添加用户之前先检查用户名的可用性。这里的UserDao也只是一个接口,jdbcUserDaoImpl是基于jdbc的实现
jdbcUserDaoImpl
private Connection conn = null;

	/**
	 * 
	 * 添加一个新用户
	 * 
	 * @param userName
	 * @return
	 */
	public boolean insertUser(User user) {

		boolean flag = false;// 插入成功与否

		StringBuffer sql = new StringBuffer(
				"insert into TB_USER(USER_NAME,USER_PWD) values('");
		sql.append(user.getUserName());
		sql.append("','");
		sql.append(user.getUserPwd());
		sql.append("')");

		System.out.println(sql);
		System.out.println(System.currentTimeMillis());

		try {
			conn = DbHelper.getConnection();
			if (DbHelper.executeUpdate(conn, sql.toString()))
				flag = true;
			else
				flag = false;
		} finally {
			DbHelper.releaseConnection(conn);
		}

		return flag;
	}

	/**
	 * 根据用户名获取用户对象
	 * 
	 * @param userName
	 * @return
	 */
	public User getUserByUserName(String userName) {
		
		User user = null;//用于返回的User对象
		ResultSet rst=null;
		StringBuffer sql = new StringBuffer("select USER_NAME from TB_USER ");
		sql.append("where USER_NAME='");
		sql.append(userName);
		sql.append("'");

		System.out.println(sql);
		System.out.println(System.currentTimeMillis());

		conn = DbHelper.getConnection();
		try {
			rst = DbHelper.executeQuery(conn, sql.toString());
			if (rst.next()) {
				user=new User(rst.getString(1));				
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DbHelper.releaseConnection(conn);
		}

		return user;

	}




问题二这样的方式有一个明显的问题,就是接口的实现和接口是耦合的(业务层和持久化层都是),尽管在这个项目里面并不是很大的问题,不过我还是想知道有没有比用factory模式加配置文件更好的方式,因为那样的话配置文件的维护简直是恶梦。
  • 描述: 时序图
  • 大小: 32.5 KB
0 请登录后投票
   发表时间:2006-10-13  
cjmm 写道
行为艺术家 写道
dao就是简单数据访问,我觉得是没有必要做一个类层次的.我感觉它的权责过轻了,实际使用时为一时方便又容易分派给它很多额外任务,所以有必要分开成两层Domain和Service.

我认为系统中各部分传递的数据都应该是Domain(也就是你说的model)中各类及其集合和组合,logic是对数据从内(java)向外(view)或从外向内的处理,service搭起流动对象与持久层之间的桥梁.我感觉这样比较清晰.

请问cjmm 怎么看?(PS:这段是后来改的,原文是楼上怎么看,回完贴才发现楼上换人了,Robbin是否觉得那个小CheckBox有些不妥啊)


加一个DAO层主要是从TDD的角度出发,对业务层进行测试时可以通过mock独立于数据库进行。
我model里面放的是没有业务逻辑只有getter和setter的POJO,在DAO层save/update/get得到的是model对象
这样子的考虑事实上是把模型与逻辑完全分离。
service里面会对model进行进一步封装,model不是提供给web的对象。

举个例子,要对用户进行管理,

package model;

public class User
{
    private String name;
    private String password;
    private long lastLoginTime;

   //以下是getter,setter和hashcode,toString,equals方法

}

package dao;
public interface UserDAO
{
  model.User getUser(String name);
  List getUsers();
  void saveOrUpdateUser(model.User user);
  void removeUser(model.User user);
}


package service.user;
public interface User
{
   String getName();
   String getPassword();
   boolean validatePassword(String pwd);
   void update();
}
public interface UserService
{
    User getUser(String name);
    User[] getUsers();
    void createUser(String name, String pwd);
    void removeUser(User user);
}

package service.user.impl
final class UserAdapter implements UserAdapter
{
  ...
}

final class UserService implements UserService
{
  ...
}

事实上在web层全部是通过接口得到对象,而在service层得实现中把model对象用adapter封装成适用于实际应用的对象提供给调用者,最大限度地在层次间进行解耦



1、我觉得领域对象到底是不是只有getter和setter倒不是很重要,有的方法还是可以放在里面的(不过这个界限我也很困惑)。你对DAO的描述让我突然发现自己在代码里的一个错误--在dao的实现里混入了逻辑
--返回用户名是否可用,还好发现了,呵呵
2、格式的问题是要用code标签就ok了
0 请登录后投票
   发表时间:2006-10-14  
hehe,多说几句吧。
1 sql拼串是安全编程大忌
2 设计不是凭空想就能想出好的设计,没有比较长时间的编程经验是做不出好的设计的
3 建议使用spring框架
0 请登录后投票
论坛首页 Java企业应用版

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