一个困惑的关于域建模的若干个问题

一个User类:


public class User{
private int ID;
private String name;
private String password;
private int GroupID;
....
public void setPassword(String password){
...
}
public void changePassword(String password){
...
}
....
}

问题1:新增一个用户,从域模型角度去看其实就是new User(),而修改用户密码就setPassword();而从另一个角度看,修改密码却可以看成User类的一个行为,可以为User类设计一个方法changePassword(),新增用户可以用一个createUser(User user)来实现。那么具体的实现在实际项目中怎样进行取舍选择?
问题2:如果使用createUser(User user)这种方式实现,那么createUser的方法应该不能放到User类中了,毕竟对象不能自己create自己,可是实体bean似乎就是这样的方式实现create??为什么??
问题3:既然User类不能create自己,那么创建一个UserManager类,包含createUser方法,这种情况下,User类的changePassword方法是否也放到UserManager类中?
问题4:如果使用new User方式来创建用户,那么删除用户的操作用什么方式进行?
问题5:怎么理解实体bean?他是域对象建模的结果么?那么怎么理解实体bean的create,remove?自己可以create,remove自己?

请banq大虾提提建议?

看了几天没人回答,我就来试试吧,我也是新手,呵呵,共同探讨。

User是个domain model,你可以用DAO模式去操作这个model
创建一个UserDAO,就是你自己的UserManager。


public interface UserDAO {
public Customer createUser(...);
public boolean updateUser(...);
public boolean deleteUser(...);
public Collection selectUser(...);
}

可是DAO只不过应该充当一个域模型和数据源之间的中介。DAO的作用不就是为域模型提供底层数据源屏蔽么。

User中不应该有public void changePassword(String password){方法

使用UserManger来创建管理User,实体Bean是域模型的一个实现结果。实体Bean中有的create,remove方法,其实这两个方法不是对Model操作,而是管理实体Bean自己的,不是操作User的。

实体bean或EJB如同一个载体,例如是太空船等,具体Model放在太空船中,是船中货物,那么对于Model来讲,船是载体,载体自己有自我管理的能力,如发动:create;停止:remove,是不是类似有生命体一样,可以自己活动的,所以对于EJB经常又有生命周期的说法。

不知是否理解?

多谢banq,ejb的create和remove是做生命周期管理用的。
不过有一个问题确实十分迷惑?怎么区分对象的操作?像public void changePassword(String password)这样的方法不应该算是域模型类User的操作么?如果是User的操作,那么按照面向对象,对象就是由属性和操作组成的,为什么放到User里不合适?如果不是User的操作,那么到底什么样的方法能算是User的操作呢?怎么划分

现在这个问题确实成了个问题了,
是不是就是所谓的Transaction Script和Domain Model的差别?我是最近看了透明的blog时候想到这确实是个问题。这个东西值得研究研究。

简单一个问题,包含的东西真是很多,这种真是那种“贫血的domain model”
看看martin fowler对这个问题的看法吧。
http://martinfowler.com/bliki/AnemicDomainModel.html

是啊,^_^,看来人家是不赞同贫血的域模型的。一个无方法域模型的确是不符合面向对象设计的思想的。

我回答的“User中不应该有public void changePassword(String password){方法,使用UserManger来创建管理User”

是基于下面这个原则:
1.EJB的Entity Bean是域模型持久化的一个实现方式,其他有JDBC、O/R Mapping等。

2.EJB的Session Bean作为Transaction Script实现。某种程度上是一种过程。

3.设计模式、可复用的部分以POJO实现,可被Session Bean调用。必要时,使得Session Bean成为接口实现参与复用设计。

这点和贫血模型是抵触的,我不赞成贫血模型,至少我现在的项目都是所谓贫血模型。

从与UML衔接来看,顺序图一般由SLSB实现,域模型由普通的Java类(POJO)实现,如只有set get的User类,实体Bean/JDO/Hibernate只是域模型持久化的一个形式。

如果在域模型中嵌入changePassword这样的方法,我以前这么做过,有兴趣者可参考我的书的第二章,Jsp/JavaBeans/JDBC实现的用户注册系统,这种情况下这么做比较简单。

但是,当引入EJB这样的组件模型(Component Model)之后,我发现使用贫血模型的好处是可以方便的实现组件化编程,因为你只要将一个域模型分离抽象出来,就可以形成一个可重用的组件(构件)库,例如,用户登陆验证每个系统都需要,现在它已经成为我的一个组件库,可以复用在多个项目。下一步就差通过SOA更有效率地输出这种重用的服务。


1.setPasswd是User类的责任,可以作为User的一个方法出现。而如何实例化一个User对象,如何新增一个用户就不是他的责任了。
2.同上
3.如果需要个UserManager类管理User,可以有createUser方法,createUser可以是这个类的责任
4.上步中createUser后UserManger肯定要持有实例化的User对象,通常是放数组或vector之类的容器里,deleteUser用户的操作就不用我讲了吧
5.尸体bean专家讲过了,偶没的说了

不知道阿拉有没理解错您的意思

这个问题也就理论上讨论讨论,实际上关系不大,也许那个flower的peaa这本书已经说过了。按我的理解,可以分为两个类,一个管理user的生存周期(UserMng),一个就是user domain(User)本身了。象创建,生成,查找,删除之类的都在UserMng里,象changePasswd应该在user里。其实可以把passwd看作一个user属性,叫setPasswd也行。当然还有象修改别人密码的需求,可以先通过find user,再user.setPasswd().

我比较同意neooen的看法,bang的部分看法我也同意,但关于他反对所谓的贫血模型,呵呵。域模型说白了就是数据的载体,应该只有自己的一些属性和属性状态变化的方法,就setter和getter方法。关于对它的系列化应该由专门负责这样事情的操作类来处理,就象上面所说的userManger一样。现在看看下面的代码:
public class User extends AbstractValueObject { private int ID; private String name; private String password; private int GroupID; .... public void setPassword(String password){ ... } public void changePassword(String password){ ... } ....}

public interface AbstractDAO
extends Serializable {
public abstract int insert(AbstractValueObject abstractvalueobject) throws
SQLException;

public abstract int update(AbstractValueObject abstractvalueobject) throws
SQLException;

public abstract int delete(Object obj) throws SQLException;

public abstract int insert(AbstractValueObject abstractvalueobject,
Connection connection) throws SQLException;

public abstract int update(AbstractValueObject abstractvalueobject,
Connection connection) throws SQLException;

public abstract int delete(Object obj, Connection connection) throws
SQLException;

public abstract AbstractValueObject findByPrimaryKey(Object obj) throws
SQLException;

public abstract AbstractValueObject findByPrimaryKey(Object obj,
String[] sqls) throws SQLException;

public abstract Collection findAll() throws SQLException;

public abstract Collection findBySql(String s) throws SQLException;

public abstract Collection findBySql(String s, Connection connection) throws
SQLException;

public abstract Page getPageBySql(String s, Integer start, Integer count) throws
SQLException;

public Collection getPageColBySql(String findSql, Integer starIndex,
Integer pageSize, Integer pageCount) throws
SQLException;

}

public UserDAO impletments AbstractDAO { .........}

public UserManager {

private startic UserDAO dao = new UserDAO

public createUser(User user) {


dao.create(user);
................

}

public changePassword(User user) {
//user已经在外面user.setPassword(newPassword);

dao.update(user);
...................
}

}

在Ejb的Entity Bean其实不是和user类似,而是和UserDao类似,不信你看看他的skelet实现类(了解RMI的理论),呵呵,里面是什么,是jdbc。所以不要误会了,虽然它能给外面提供域模型实例集合。看下面:
public java.lang.String ejbCreate(ForumValueObject vo) throws CreateException {
System.out.println("ejbCreate(vo)");
String forumId = vo.getForumId();
String forumName = vo.getForumName();
String forumCnname = vo.getForumCnname();
String forumProductid = vo.forumProductid;
com.topfounder.common.database.TPClob tmpTPClob = vo.getContent();
String content = tmpTPClob == null ? null : tmpTPClob.getContent();
String admin = vo.getAdmin();
java.sql.Date registerdate = vo.getRegisterdate();
String topic = vo.topic;
String remark = vo.getRemark();
ejbCreate(forumId, forumName, forumCnname, forumProductid, content, admin,
registerdate, topic, remark);
return null;
}

其实 它就是系列化域模型,这里JDBC你没有看到,但在它的具体实现(skelet)里就看到了。
我反对批评所谓的贫血模型,在贫血模型加上不伦不类的逻辑操作才是怪物,我在说一下,域模型就是一个数据载体。


实在不同苟同上面这位兄弟的观点“在贫血模型加上不伦不类的逻辑操作才是怪物”,请阅读domain-model design这本书。目前普遍存在两种设计方式,一种是使用Manager也就是service方式来管理业务逻辑,还有一种就是楼上所不赞成的domain object来管理业务逻辑。其实service的方式并没有什么不好,但是它的职责应该描述的更清晰,它不应该是实现全部的业务逻辑,而只是调用各个domain object的方法,对外来提供服务而已,可以说它是代理domain object的方法或者是系统facade。真真实现业务逻辑纳入domain object并没有什么奇怪,它真实的反映了领域模型的行为,更符合OO,当然回带来更多的好处。

谢谢nekesai的肯定,不过看了您贴出来的代码,乱乱的没怎么看明白。
不过看到了一个问题

public UserManager {

private startic UserDAO dao = new UserDAO

public createUser(User user) {


dao.create(user);
................

}

public changePassword(User user) {
//user已经在外面user.setPassword(newPassword);

dao.update(user);
...................
}


}
这个方法好像是userManager的,和dao发生了什么关系,还和User有什么关系,我觉的如果真的要用userManager.changePassword,还是把changePassword委托给User来做最好。

不知道我有没理解错您的意思