|
锁定老贴子 主题: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-09-26
谢谢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.javaeye.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-10
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();
}
}
|
|
| 返回顶楼 | |













