论坛首页 Java企业应用论坛

为什么java里不能把域对象和DAO合并,rails里面就可以?

浏览 29172 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-03-03  
我的老板(外国人)跟我们一起吃广式茶的时候有个大发现:吃西餐的时候如果你需要什么你够不着的共享的东西,是请别人把那个共享的盘子递过来;吃中餐你需要添一点炒面之类的,是把你的小碗递过去。
看起来好像是文化差别,其实背后的原因很简单:中餐你自己的小碗很轻,西餐你自己面前是个很沉的大盘子。
Java不能做,而Rails可以做,原因(至少一定程度上)也是一样:轻重不同
Rails一个model才几行代码,Java一个model多少行代码?
随便拿10个属性的一个model,属性要10行,getter setter每个属性8行这就是80行,再加default constructor, equals, hashCode,随随便便就100+行了,这还没算注释。这要是再把业务逻辑和Dao合并进来,不就太沉重了吗?
轻和重是现象,而现象背后,是Ruby的描述能力远远强过Java。这个要展开了说就不得了了,能写厚厚一本书。简单举例说吧,一个method_missing,就把ActiveRecord给玩活了;Java你照样extends 一个基类,收获的是“依赖”而没有什么好处。总而言之吧,Java学不了Rails的玩儿法。
1 请登录后投票
   发表时间:2007-03-03  
aardvark 写道
我的老板(外国人)跟我们一起吃广式茶的时候有个大发现:吃西餐的时候如果你需要什么你够不着的共享的东西,是请别人把那个共享的盘子递过来;吃中餐你需要添一点炒面之类的,是把你的小碗递过去。
看起来好像是文化差别,其实背后的原因很简单:中餐你自己的小碗很轻,西餐你自己面前是个很沉的大盘子。
Java不能做,而Rails可以做,原因(至少一定程度上)也是一样:轻重不同
Rails一个model才几行代码,Java一个model多少行代码?
随便拿10个属性的一个model,属性要10行,getter setter每个属性8行这就是80行,再加default constructor, equals, hashCode,随随便便就100+行了,这还没算注释。这要是再把业务逻辑和Dao合并进来,不就太沉重了吗?
轻和重是现象,而现象背后,是Ruby的描述能力远远强过Java。这个要展开了说就不得了了,能写厚厚一本书。简单举例说吧,一个method_missing,就把ActiveRecord给玩活了;Java你照样extends 一个基类,收获的是“依赖”而没有什么好处。总而言之吧,Java学不了Rails的玩儿法。

我说Java可以做得更好, 哈哈. 看看你继承一个持久根基类可以得到什么:
/**
 * The base class for all persistent classes managed by TOB.
 * <p>
 * See the <a href="http://tob.ableverse.org/model.html">Object-Relation-Kin
 * Model</a>.
 * 
 * @author Compl
 * 
 */
public class TheObject
{
//.....
    /**
     * Test whether this instance is a fetus or has been born.
     * 
     * @return true if already born, false if still a fetus
     */
    public boolean isBorn()
    {
        return false;
    }

    /**
     * Get the managing TOB.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public <Q extends TheObjectBase.Querier> TheObjectBase<Q> getTOB()
            throws ObjectNotBornException
    {
        throw new ObjectNotBornException();
    }

    /**
     * Get the persistent object's id.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public long getID() throws ObjectNotBornException
    {
        throw new ObjectNotBornException();
    }

    /**
     * Get the UTC milliseconds the persistent object was created.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public long getTimeCreated() throws ObjectNotBornException
    {
        throw new ObjectNotBornException();
    }

    /**
     * Get the UTC milliseconds the persistent object was modified.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public long getTimeModified() throws ObjectNotBornException
    {
        throw new ObjectNotBornException();
    }

    /**
     * Kill the persistent object.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public void die() throws ObjectDependedException
    {
        throw new ObjectNotBornException();
    }

    /**
     * Test whether the persistent object has been killed.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public boolean isDead() throws ObjectNotBornException
    {
        throw new ObjectNotBornException();
    }

    /**
     * Get the last committed version of this object.
     * <p>
     * <code>null</code> will be returned if not invoked within a
     * transactional context of TOB.
     * <p>
     * The retunred object is not supposed to be a born object, so
     * {@link ObjectNotBornException} will be thrown by several methods declared
     * to throw this exception.
     * <p>
     * WARNING: You should NEVER make ANY change to the object returned.
     * 
     * @param <T>
     *            actual type of the persistent object
     * 
     * @return the last committed version of this object, or null if not in a
     *         transactional context of TOB
     */
    protected <T extends TheObject> T previousSelf()
    {
        return null;
    }

    /**
     * persistent subclasses override this to get notified before born, upon
     * {@link TheObjectBase#birth(TheObject)} is called. This is the right place
     * to perform persistent object creation validation, throw a
     * {@link av.tob.FetalDeathException} to prevent a successful birth if
     * invalid conditions detected.
     * <p>
     * Note: checks here should not take long because this operation is
     * synchronized with the whole TOB.
     */
    protected void beingBornNotify(TheObjectBase<?> tob)
            throws FetalDeathException
    {
    }

    /**
     * persistent subclasses override this to get notified after successfully
     * born, upon {@link TheObjectBase#birth(TheObject)} is called.
     * <p>
     * Note: checks here should not take long because this operation is
     * synchronized with the whole TOB.
     */
    protected void bornNotify()
    {
    }

    /**
     * persistent subclasses override this to get notified when updates are
     * about to commit.
     */
    protected void committingNotify()
    {
    }

    /**
     * persistent subclasses override this to get notified when updates are
     * committed. This will be invoked before {@link #writtenNotify()} or
     * {@link #changedNotify()}.
     * <p>
     * Caution: NONE further immediate change should be made within this method,
     * or data consistency is likely to corrupt.
     */
    protected void committedNotify()
    {
    }

    /**
     * persistent subclasses override this to get notified when updates have
     * been committed and the updates include persistent fields change.
     * <p>
     * Caution: NONE further immediate change should be made within this method,
     * or data consistency is likely to corrupt.
     */
    protected void writtenNotify()
    {
    }

    /**
     * persistent subclasses override this to get notified when updates have
     * been committed and the updates only include kin fields change.
     * <p>
     * Caution: NONE further immediate change should be made within this method,
     * or data consistency is likely to corrupt.
     */
    protected void changedNotify()
    {
    }

    /**
     * persistent subclasses override this to prevent being killed by throwing
     * an {@link ObjectDependedException}.
     */
    protected void killingNotify() throws ObjectDependedException
    {
    }

    /**
     * persistent subclasses override this to get notified when killed.
     */
    protected void killedNotify()
    {
    }

    /**
     * persistent subclasses override this to get notified when restored from
     * the backend storage.
     */
    protected void restoredNotify()
    {
    }

    /**
     * persistent subclasses override this to get notified after being restored
     * from the backend storage and set online.
     */
    protected void onlineNotify()
    {
    }

    /**
     * TOB will clone persistent object field values from another at some
     * particular point.
     * 
     * <li> when commit, clone from the delegate object to the core object.
     * <li> when rollback, clone from the core object to the delegate object.
     * <li> initial constructions of delegate object and core object will clone
     * from its peer.
     * 
     * TOB only clone field values for one level, which in most case is properly
     * enough. But there are still classes who need clone in deeper depth. Those
     * should override this method to get notified whenever a clone has been
     * done to this instance and perform proper additional deeper clone
     * operations.
     * 
     * @param src
     *            the source object from which the clone has taken place
     */
    protected void clonedFromNotify(TheObject src)
    {
    }

    /**
     * This method will be overrided by TOB generated code and return the
     * calculated count of persistent fields of the actual persistent class.
     * <p>
     * -1 will be returned by a non-born object.
     * 
     * @return how many persistent fields the persistent class actually has, or
     *         -1 if not invoked against a born object.
     */
    public int getPersistentFieldCount()
    {
        return -1;
    }

    /**
     * Persistent classes normally override this method to return a fixed
     * priority number for the class extent.
     * 
     * See {@link OnlinePriority} for more information.
     * 
     * Note swap engines will override this method to return its
     * <em>remembered</em> value of particular objects, in this case the value
     * returned by the persistent class code serves as the default value upon
     * birth of its instances.
     * 
     * @return the online priority for this object. approximately 100 * the
     *         percentage of free memory in jvm above which this object should
     *         be kept <em>online</em>.
     * 
     * @see OnlinePriority
     */
    public short getOnlinePriority()
    {
        return OnlinePriority.NORMAL;
    }

    /**
     * Swap engines will trap invocation to this method to update the priority
     * information they stored natively.
     * <p>
     * The instance must be of a swappable persistent class and has been born by
     * a TOB. Or an {@link ObjectNotBornException} will be thrown.
     * 
     * @throws ObjectNotBornException
     */
    public void setOnlinePriority(short priority) throws ObjectNotBornException
    {
        throw new ObjectNotBornException();
    }
}

编译时会自动生成应用持久类的派生类去实现所有这些的支持逻辑, 应用只管用, 可以很轻快.

另外我认为把每个field都expose为可读写property的做法是产生贫血模型的绝好途径, 完全没有数据封装概念了, 和定义一个C的struct没啥差别, 所以也是一个罪恶源头. 而实际上JavaBean规范最初是为那些UI Design时可以拖拽组装的界面组件设计的, 没想被滥用到现在这样...
0 请登录后投票
   发表时间:2007-03-03  
complystill 写道

我说Java可以做得更好, 哈哈. 看看你继承一个持久根基类可以得到什么:
...

我说过了,可以得到“依赖“,没什么好的。
0 请登录后投票
   发表时间:2007-03-03  
complystill 写道


另外我认为把每个field都expose为可读写property的做法是产生贫血模型的绝好途径, 完全没有数据封装概念了, 和定义一个C的struct没啥差别, 所以也是一个罪恶源头. 而实际上JavaBean规范最初是为那些UI Design时可以拖拽组装的界面组件设计的, 没想被滥用到现在这样...


这一点是错误的,field是不可以覆盖的,而方法是可以覆盖的,Programming Ruby在对象一节对于这个解释的很清楚。
0 请登录后投票
   发表时间:2007-03-03  
JavaInActoin 写道
持久化不属于领域层的范围,属基础层,如果把持久化这种琐碎的细节都绑在Domain对象中,Domain Model就失去了意义,隔离出一个干净精练的Domain层会比较好,那里是软件的核心。
Java这样清晰而严谨的语言是实现Domain Model的极佳选择,很容易理解和交流,把Domain Model和Hibernate、Spring联系到一起应该是过于放大了Domain Model的职责范围。
Domain Model不能太胖,也不能太瘦,能够完整地描述核心业务功能就刚刚好,现在流行的层次结构中的Entity只是简单的属性集合,把所有的业务功能交给Service,这就制造了一个瘦Domain Model。Domain Model里的Entity应该具有职责范围内的业务功能,而且Domain Model中的对象不止是Entity一种,还包括Domain Service,比如需要多个Domain对象合作完成的业务功能不方便放在哪个Domain对象中,就需要单独的Domain Service,当然,分清Domain层Service和应用层的Service是有一定难度的。


认同这样的观点,不知道大牛们为何对次没评论而专注于“对抗式”的讨论,难道是因为某些概念和“某某大牛”书籍中的定义不同?
拿java和ruby的代码数量来比较,个人觉得也欠妥当。
0 请登录后投票
   发表时间:2007-03-03  
jianfeng008cn 写道

认同这样的观点,不知道大牛们为何对次没评论而专注于“对抗式”的讨论,难道是因为某些概念和“某某大牛”书籍中的定义不同?
拿java和ruby的代码数量来比较,个人觉得也欠妥当。

你当然有权把一个class写成几千行,也有权用几百行代码去实现别人几十行就实现的东西。你绝对有那个权利。
0 请登录后投票
   发表时间:2007-03-03  
花花公子 写道
complystill 写道


另外我认为把每个field都expose为可读写property的做法是产生贫血模型的绝好途径, 完全没有数据封装概念了, 和定义一个C的struct没啥差别, 所以也是一个罪恶源头. 而实际上JavaBean规范最初是为那些UI Design时可以拖拽组装的界面组件设计的, 没想被滥用到现在这样...


这一点是错误的,field是不可以覆盖的,而方法是可以覆盖的,Programming Ruby在对象一节对于这个解释的很清楚。


我说的 "滥用" 是指那些在 getter/setter 中除了读/写field以外, 根本没有更多逻辑, 也从来不被重载的模式.
0 请登录后投票
   发表时间:2007-03-03  
aardvark 写道
complystill 写道

我说Java可以做得更好, 哈哈. 看看你继承一个持久根基类可以得到什么:
...

我说过了,可以得到“依赖“,没什么好的。


大家觉得那些直接可以调用, 还有可以重载加入应用处理逻辑的方法不好么?

这可是相当于传统上你得用 SP 写 Trigger 才能实现的功能啊.
0 请登录后投票
   发表时间:2007-03-03  
jianfeng008cn 写道
JavaInActoin 写道
持久化不属于领域层的范围,属基础层,如果把持久化这种琐碎的细节都绑在Domain对象中,Domain Model就失去了意义,隔离出一个干净精练的Domain层会比较好,那里是软件的核心。
Java这样清晰而严谨的语言是实现Domain Model的极佳选择,很容易理解和交流,把Domain Model和Hibernate、Spring联系到一起应该是过于放大了Domain Model的职责范围。
Domain Model不能太胖,也不能太瘦,能够完整地描述核心业务功能就刚刚好,现在流行的层次结构中的Entity只是简单的属性集合,把所有的业务功能交给Service,这就制造了一个瘦Domain Model。Domain Model里的Entity应该具有职责范围内的业务功能,而且Domain Model中的对象不止是Entity一种,还包括Domain Service,比如需要多个Domain对象合作完成的业务功能不方便放在哪个Domain对象中,就需要单独的Domain Service,当然,分清Domain层Service和应用层的Service是有一定难度的。


认同这样的观点,不知道大牛们为何对次没评论而专注于“对抗式”的讨论,难道是因为某些概念和“某某大牛”书籍中的定义不同?
拿java和ruby的代码数量来比较,个人觉得也欠妥当。


抛开具体的实现技术来说,这话从理论上来说是没有任何问题的,所以没有什么好评论的。但是落实到具体的编码实现,光靠嘴巴说说是不解决问题的。用不用spring/hibernate这个不重要,你可以不用spring/hibernate,不管用不用,只要你能用Java给我实现一个好的Rich domain model出来就行,但问题是现在Java要做到这一点是很困难的,而且即使实现了,代码结构也很不好,如果你不信,就按照我提的要求用Java代码把我上面描述的场景写出来给我们看看。

说句题外话:

Martin Folwer在自己的bliki当中首次提出贫血的领域模型并且给予批评的时候,ThoughtWorks公司内部就引起了很大的争论,争论的焦点就是Martin你现在说贫血的领域模型不好,那我们现在用Java做项目,我怎么去实现充血的领域模型呢?对此Martin没有答案。

后来ThoughtWorks有个员工通过扩展Hibernate的拦截器注入spring容器引用来实现了对于领域模型的Dao注入,从而让Hibernate的领域模型终于可以Rich起来了。当然这种方式局限性很大,而且很麻烦。自从spring2.0推出以后,另外一种通过AspectJ静态织入的方式更加被推崇了。

但不论用什么框架,对于Java来说,实现充血的领域模型是很麻烦的事情,得不到充血模型的好处不说,反而处处暴露Java语言的短处。然而RoR确可以轻易实现充血的领域模型,而且可以充分发挥充血模型的好处。

这里我们不是探讨充血模型好不好,贫血模型好不好的,而是探讨Java适合不适合充血模型,是否方便的表达domain logic的,既然落实到具体的技术实现,你还非要空对空的谈理论,还非要我们承认你对理论的理解是正确的,不是牛唇不对马嘴吗?
0 请登录后投票
   发表时间:2007-03-05  
robbin 写道
给你们出道题目吧:

User类,有属性name, password, gender, department, salary, tasks(员工当前的工作任务)

Task类,有属性name, duration, owner(谁来做)

有这样的业务逻辑:
1、统计某个部门的薪水总和
2、统计某个员工的工作量
3、统计某个部门的总工作量

请用Java语言进行建模,可以使用DAO接口隔离持久化操作,请应用Martin Fowler的PoEAA中的Rich Domain Model模式。请给出你的Java代码



直接用基于jdbc的包装,也就区区三句sql文,三行java代码
说句robbin可能不爱听的话,在我们这里,自从一年前ruby被我一板拍死之后,到现在连复燃的死灰都没有。ruby真的很火吗?好像不见得吧。另:je推ruby好像已经很长时间了。
0 请登录后投票
论坛首页 Java企业应用版

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