论坛首页 Java企业应用论坛

domain model的延伸讨论

浏览 73411 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-03-04  
另外说起做Web应用来, 对于单服务器环境, 看看WoW就知道TOB应用的成熟程度了, 管理界面也是JSP的, Applet的xml通信模式和AJAX没有本质差别.

基于HBI的分布式模式也会很快成熟起来.
0 请登录后投票
   发表时间:2007-03-04  
robbin写道:
“Java难于实现充血模型的根本原因还是在于:Java是静态类型的语言,难以运行期任意的动态改变其行为,所以必须依赖外部容器例如IoC进行对象依赖组装,也必须依赖特定的ORM框架进行增强其行为,这些容器和框架本质上都是通过动态代理方式来增强类,其结果就是对象的设计必须符合容器对它的要求,从而限制你追求理论上更完美模型的可能性。至于继承不继承类,这个根本无关紧要。”

re:你能否回答一下RR的哪些动态特性支持了其优越的域对象模型?而这些动态特性是java中无可企及的?

“不知道大家有没有想过,为什么Java这么强调面向接口编程?面向接口编程为什么对Java这么重要?但是对于动态面向对象语言ruby来说,却根本不需要接口这种概念。这是因为在Java中是类型决定行为,所以类型的地位很重要,你一继承,对象的行为就被限制死了,所以Java很忌讳继承的使用;但是ruby的类型不决定行为,所以随便你怎么继承,也不会限制对象的行为。”

re:这种灵活完全是优点吗?我看未必。对于简单应用,你想怎么应用就怎么应用,但是应用复杂了之后呢?随意的继承,会是怎样的一种后果?是否会产生系统消化不良,导致运行效率问题,还有后期无法维护呢?
0 请登录后投票
   发表时间:2007-03-04  
大牛们,研究一下SpringXT,看看SpringXT能不能解决RICH DOMAIN MODEL的问题
0 请登录后投票
   发表时间:2007-03-04  
接chinaet的话题,从java开源大全网站获取SpringXT的摘要信息如下:

“SpringXT是Spring框架的一个扩展用于开发richer domain models与richer user interfaces的应用程序。采用Domain Driven Design设计原则。为此SpringXT提供两个框架SpringXT Modeling Framework:提供一些组件来开发rich domain model(它集中了所有业务逻辑,规则和约束,完全独立于应用程序的其它部分)并能够让它们与其它应用软件分层"优雅"结合。SpringXT Ajax Framework:一个完全与Spring MVC集成在一起,基于事件的Ajax框架。”

java开源大全网址:
http://www.open-open.com/open184107.htm

springxt官方网址:
http://springxt.sourceforge.net/index.php/Main_Page
0 请登录后投票
   发表时间:2007-03-04  
JavaInActoin 写道
robbin 写道

你这个领域模型是根本跑不起来的。数据库根本就没有department这个表,你的department的users属性根本就是null,一跑就会出错。你的user.addTask方法也执行不了,没有Dao的支持,你再调用addTask,数据库里面也不增加记录。UserManager也不应该存在,这是属于user的domain logic。
请不要拿一个根本不能运行的错误代码出来,请先在你本地搭建一个实际的web项目,自己测试通过了,再贴出来。

单独的领域模型当然跑不起来,和其它层装配起来就跑的很以欢畅了。
我写的Domain Model是根据Eric Evans的观点来的,很多持久化(包括事务)的操作超越了业务逻辑,需要被安排在应用服务层。
你写的那段不是MF的充血模型,而是事务脚本。
robbin 写道

请不要拿一个根本不能运行的错误代码出来,请先在你本地搭建一个实际的web项目,自己测试通过了,再贴出来。

这个不是讨论问题的最佳方法,太累了,你写的代码同样不能运行
robbin 写道

    public employee(String username, String department) {   


寻找这样的错误是没有意义的,只要思路正确,在座的各位都可以把代码调试好。


OO思想或者实现不能应用到系统所有地方,JavaInActoin这么说也是很有道理的。
0 请登录后投票
   发表时间:2007-03-04  
谢谢Robbin好文
0 请登录后投票
   发表时间:2007-03-04  
robbin 写道


可以省略DAO,也可以省略Service,请你把符合rich domain object的Java代码贴出来,别光说不练。



不知道大家有没有注意过 .net的 castle http://www.castleproject.org/ 项目?它通过对nhibernate的集成(使用标注)实现了.net上的ActiveRecord。

代码类似这样

// Copyright 2004-2006 Castle Project - http://www.castleproject.org/
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace BlogSample
{
	using System;
	using System.Collections;

	using Castle.ActiveRecord;

	[ActiveRecord]
	public class Blog : ActiveRecordBase
	{
		private int id;
		private String name;
		private String author;
		private IList posts = new ArrayList();

		public Blog()
		{
		}

		public Blog(String name)
		{
			this.name = name;
		}

		[PrimaryKey]
		public int Id
		{
			get { return id; }
			set { id = value; }
		}

		[Property]
		public String Name
		{
			get { return name; }
			set { name = value; }
		}

		[Property]
		public String Author
		{
			get { return author; }
			set { author = value; }
		}

		[HasMany(typeof(Post), 
			Table="Posts", ColumnKey="blogid", 
			Inverse=true, Cascade=ManyRelationCascadeEnum.AllDeleteOrphan)]
		public IList Posts
		{
			get { return posts; }
			set { posts = value; }
		}

		public static void DeleteAll()
		{
			ActiveRecordBase.DeleteAll(typeof(Blog));
		}

		public static Blog[] FindAll()
		{
			return (Blog[]) ActiveRecordBase.FindAll(typeof(Blog));
		}

		public static Blog Find(int id)
		{
			return (Blog) ActiveRecordBase.FindByPrimaryKey(typeof(Blog), id);
		}
	}
}


如果使用.net 2.0,还可以利用范型把所有的ActiveRecordBase去掉,换成自己本身的类名。
.net可以做到,java为什么做不到?


当然,这个ActiveRecord比起ror的来,差的老远:

*不能自动从数据库映射字段
*hibernate的缺点(长长的sql等)它都有
*数据库改了字段它必须修改代码

大部分缺点都是由java和.net静态语言的特性造成的

不过如果可以忍受这些,java和.net也是可以做到充血模型的
0 请登录后投票
   发表时间:2007-03-04  
robbin 写道

不知道大家有没有想过,为什么Java这么强调面向接口编程?面向接口编程为什么对Java这么重要?但是对于动态面向对象语言ruby来说,却根本不需要接口这种概念。这是因为在Java中是类型决定行为,所以类型的地位很重要,你一继承,对象的行为就被限制死了,所以Java很忌讳继承的使用;但是ruby的类型不决定行为,所以随便你怎么继承,也不会限制对象的行为。

duck typing也是typing,我琢磨着即便是动态语言,类型决定行为(或者行为决定类型)还是必要的,区别在于是不是有显式的类型。
但是大型的python应用中如zope,twisted,peak等大都引入了自己的interface方式, python3000也在讨论引入interface的某个类似物(貌似解决方案讨论反复了多次).
另外一点,"设计模式"这本书出版(特别是酝酿)的时候,java的毛还没长全,里面的主打语言还是c++/smalltalk,后者就是动态语言。而此时四大仙就有了面向接口编程的说法.
0 请登录后投票
   发表时间:2007-03-04  
newman 写道
昨天晚上就为这个话题在网上逗留了许久,今天再一看发现robbin同学很是快手,把这个话题又重新整理了一遍,真是辛苦,虽然争论依然激烈,不过很多问题也得到了澄清,看着各位大侠在场上刀来剑往,我想在这里说上几句:
1.这个问题的来源是fatzhen发的一个名为"主题:   为什么java里不能把域对象和DAO合并,rails里面就可以?",原贴子的地址是http://www.iteye.com/topic/56949。
2.在这篇贴子里,robbin还提到了另一篇精彩的文章叫“完美就是生产力”,一位老兄在半夜挑灯夜战抽烟搞出来的一篇文章,我建议参与讨论的网友如果对ruby不够了解的去看看,我看了后是很有收获的,很多背景问题(因为我是搞java的,rr只是搞了些皮毛)能搞清楚。
3.讨论的问题还是域对象模型在两种语言中的支持和实现优劣,而不是某某要取代某某。
4.域模型比oo模型要高级,对业务的描述性更好,也利于业务的计算实现,rr对域模型的支持和实现我认为是要优于java,但是,java也未必需要如rr一般的方法去实现域对象模型,正好比一个使刀的,一个用剑的,没必要说非得弃剑用刀或者弃刀用剑,尤其是java语言和rr语言在本质和风格上面相差太大。
5.代码强阅读性和LOC少是rr的最大热点,当然ActiveRecord做得也是非常不错,但是Java同样有很多出色的优点。
6.贫血模型还是涨血模型,我看还是根据实际应用来论,robbin也对这个问题做了分析,我也不多说了,我认为分析得还是很到位。
7.既然是刀来剑往,我想没有一个人能够100%保持彬彬有礼的形象,出言不逊在所难免,但是既然都是武林中人,能够互相切磋技艺心法,实在是人生之幸事,那些激昂言语也大可在一笑中抛诸脑后。


此兄的观点我有几点不同意(我是搞java的)


我认为领域模型就是OO模型在实际企业开发环境下的表现形式,根本就不是谁比谁高级的问题,领域模型是在企业开发中实现OO模型的最自然的形式,所谓道法自然,OO的精髓不久是模拟现实么,java在这方面确实不完美,贫血模型在任何一个比hello world稍微复杂点的系统开发中暴露的种种弊病,我想做过几个项目的人都能感觉出来,我认为领域模型就是企业开发的王道,准备开始学习ror.
0 请登录后投票
   发表时间:2007-03-05  
public class Department {

	public static void employee(String name, String department) {
		User.create(name,department);
	}
	
}


@Entity
public class Kind {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public int id = -1;

	public String name;

	public Kind(String name) {
		this.name = name;
	}

	public static Kind create(String name) {
		Kind kind = new Kind(name);
		Context.em.persist(kind);
		return kind;
	}

	public void addBatchTaskToUsers(String task) {
		List<User> users = users();
		for (User user : users) {
			Task taskEntity = new Task(task, this);
			Context.em.persist(taskEntity);
			user.applyTask(taskEntity);
		}
	}

	public List<User> users() {
		Query query = Context.em
				.createQuery("select distinct u from User as u inner join u.kinds as kind where kind = ?1");
		query.setParameter(1, this);
		return query.getResultList();
	}

}


@Entity
public class Task {
	public Task(String task,Kind kind) {
		this.kind = kind;
		this.task = task;
	}

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public int id = -1;
	
	public Date startTime;
	
	public Date endTime;
	
	public String task;
	
	@ManyToOne
	public User owner;
	
	@ManyToOne
	public Kind kind;

	public static Task create(String name, Kind kind) {
		Task task = new Task(name,kind);
		Context.em.persist(task);
		return task;
	}

	private static Date getCurrentMonthBegin()
	{
		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.DAY_OF_MONTH,0);
		Date begin = calendar.getTime();
		return begin;
	}
	
	public static Date getCurrentMonthEnd()
	{
		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getMaximum(Calendar.DAY_OF_MONTH));
		Date end = calendar.getTime();
		return end;
	}
	
	public static List<Task> allTask_CurrentMonth() {
		Date begin = getCurrentMonthBegin();
		Date end = getCurrentMonthEnd();
		Query query = Context.em.createQuery("from Task as t where t.startTime >= ?1 and t.startTime <= ?2");
		query.setParameter(1,begin);
		query.setParameter(2,end);
		return query.getResultList();
	}

	public static List<Task> processingTasks_CurrentMonth() {
		Date begin = getCurrentMonthBegin();
		Date end = getCurrentMonthEnd();
		Query query = Context.em.createQuery("from Task as t where t.startTime >= ?1 and t.startTime <= ?2 and t.endTime is null");
		query.setParameter(1,begin);
		query.setParameter(2,end);
		return query.getResultList();
	}

	public static List<Task> processedTasks_CurrentMonth() {
		Date begin = getCurrentMonthBegin();
		Date end = getCurrentMonthEnd();
		Query query = Context.em.createQuery("from Task as t where t.endTime >= ?1 and t.endTime <= ?2 ");
		query.setParameter(1,begin);
		query.setParameter(2,end);
		return query.getResultList();
	}
}


@Entity
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public int id = -1;

	public String name;

	public String department;

	@OneToMany
	public List<Kind> kinds = new ArrayList<Kind>();

	public User(String name, String department) {
		this.name = name;
		this.department = department;
	}

	public Tasks tasks = new Tasks(this);

	class Tasks {
		private User user;

		public Tasks(User user) {
			this.user = user;
		}

		public Task find_by_name(String task) {
			Query query = Context.em
					.createQuery("from Task t where t.owner = ?1 and t.task = ?2");
			query.setParameter(1, user);
			query.setParameter(2, task);
			return (Task) query.getSingleResult();
		}

		public List<Task> processing_tasks() {
			Query query = Context.em
					.createQuery("from Task t where t.startTime <= ?1 and t.owner = ?2 and t.endTime is null");
			query.setParameter(1, new Date());
			query.setParameter(2, user);
			return query.getResultList();
		}

		public boolean detectProcessingTask(String task) {
			for(Task taskEntity:processing_tasks())
			{
				if(task.equals(taskEntity.task))
				{
					return true;
				}
			}
			return false;
		}
	}

	public static User find_By_Name_And_Department(String name,
			String department) {
		Query query = Context.em
				.createQuery("from User u where u.name = ?1 and u.department = ?2");
		query.setParameter(1, name);
		query.setParameter(2, department);
		return (User) query.getSingleResult();
	}

	public static User create(String name, String department) {
		User user = new User(name, department);
		Context.em.persist(user);
		return user;
	}

	public void applyTask(Task task) {
		task.owner = this;
		task.startTime = new Date();
		Context.em.persist(task);
	}

	public static List<Task> all_processing_tasks() {
		Query query = Context.em
				.createQuery("from Task t where t.startTime <= ?1  and t.endTime is null");
		query.setParameter(1, new Date());
		return query.getResultList();
	}

	public void endTask(Task task) {
		task.endTime = new Date();
	}
}

  • project.zip (10.1 KB)
  • 描述: eclipse项目文件,包括源码及测试代码. 库放在另一个附件,有3M多,可能传不上去了
  • 下载次数: 336
  • lib.zip (3.1 MB)
  • 描述: 依赖库文件
  • 下载次数: 304
1 请登录后投票
论坛首页 Java企业应用版

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