锁定老帖子 主题:domain model的延伸讨论
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-03-04
另外说起做Web应用来, 对于单服务器环境, 看看WoW就知道TOB应用的成熟程度了, 管理界面也是JSP的, Applet的xml通信模式和AJAX没有本质差别.
基于HBI的分布式模式也会很快成熟起来. |
|
返回顶楼 | |
发表时间:2007-03-04
robbin写道:
“Java难于实现充血模型的根本原因还是在于:Java是静态类型的语言,难以运行期任意的动态改变其行为,所以必须依赖外部容器例如IoC进行对象依赖组装,也必须依赖特定的ORM框架进行增强其行为,这些容器和框架本质上都是通过动态代理方式来增强类,其结果就是对象的设计必须符合容器对它的要求,从而限制你追求理论上更完美模型的可能性。至于继承不继承类,这个根本无关紧要。” re:你能否回答一下RR的哪些动态特性支持了其优越的域对象模型?而这些动态特性是java中无可企及的? “不知道大家有没有想过,为什么Java这么强调面向接口编程?面向接口编程为什么对Java这么重要?但是对于动态面向对象语言ruby来说,却根本不需要接口这种概念。这是因为在Java中是类型决定行为,所以类型的地位很重要,你一继承,对象的行为就被限制死了,所以Java很忌讳继承的使用;但是ruby的类型不决定行为,所以随便你怎么继承,也不会限制对象的行为。” re:这种灵活完全是优点吗?我看未必。对于简单应用,你想怎么应用就怎么应用,但是应用复杂了之后呢?随意的继承,会是怎样的一种后果?是否会产生系统消化不良,导致运行效率问题,还有后期无法维护呢? |
|
返回顶楼 | |
发表时间:2007-03-04
大牛们,研究一下SpringXT,看看SpringXT能不能解决RICH DOMAIN MODEL的问题
|
|
返回顶楼 | |
发表时间: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 |
|
返回顶楼 | |
发表时间: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这么说也是很有道理的。 |
|
返回顶楼 | |
发表时间:2007-03-04
谢谢Robbin好文
|
|
返回顶楼 | |
发表时间: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也是可以做到充血模型的 |
|
返回顶楼 | |
发表时间:2007-03-04
robbin 写道 不知道大家有没有想过,为什么Java这么强调面向接口编程?面向接口编程为什么对Java这么重要?但是对于动态面向对象语言ruby来说,却根本不需要接口这种概念。这是因为在Java中是类型决定行为,所以类型的地位很重要,你一继承,对象的行为就被限制死了,所以Java很忌讳继承的使用;但是ruby的类型不决定行为,所以随便你怎么继承,也不会限制对象的行为。 duck typing也是typing,我琢磨着即便是动态语言,类型决定行为(或者行为决定类型)还是必要的,区别在于是不是有显式的类型。 但是大型的python应用中如zope,twisted,peak等大都引入了自己的interface方式, python3000也在讨论引入interface的某个类似物(貌似解决方案讨论反复了多次). 另外一点,"设计模式"这本书出版(特别是酝酿)的时候,java的毛还没长全,里面的主打语言还是c++/smalltalk,后者就是动态语言。而此时四大仙就有了面向接口编程的说法. |
|
返回顶楼 | |
发表时间: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. |
|
返回顶楼 | |
发表时间: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(); } } |
|
返回顶楼 | |