互联网解决方案咨询

梦想有多大路就会有多远:作一颗IT量子
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在系统开发中关于实体的问题

Posted on 2008-07-23 22:06  互联网粒子  阅读(323)  评论(0编辑  收藏  举报

在目前的工作,主要是针对SNS(网络社区)的开发,有同事问,现在系统中,把数据实体独立出来,在开发上,除了在函数调用时,采用传对象的方式外没感觉到他有什么其它方面的优势呢?

这个问题,其实要从多个方面来看,从写代码本身上来说,最直接的感受就是在方法调用时,在方法参数列表中传的都是实体对象, 这样做的目的,是为了免"代码地震"的苦恼,通常业务需求的变化都会引起业务对象本身的变化,把变化的东西独立隔离出来,如果方法参数都是以传值的形式来处理,当某一个业务需求变化时,有可能需要改动某一个方法的参数列表,这样一来的话,所有引用到这个方法的类都会更动,有的人说可以用重载,这个办法也是可以,便这样的重载有多少,代码的很臃肿。

对于"DDD"设计领域来看:

领域的含义:

简单的说,每个软件程序都会与其用户的活动或兴趣相关,其中使用程序的主要环境称为软件的“领域”。

领域中形形色色的业务逻辑构成了软件丰富多采的行为。举例来说,银行财务系统中,领域逻辑就包括了诸如开户,转帐等等操作。可能你会说,网站程序 员很少会接触银行系统,这样的例子不够浅显,举一个更常见的例子,大凡程序员应该都接触过文章管理系统,它里面的置顶,加精等操作就是领域逻辑。这样 看来,似乎用例对应的动作都是领域逻辑了,但是答案是否定了,比如说,文章管理系统中保存文章往往就不是领域逻辑,因为它只是一个和持久化相关的动作而 已,是纯粹的技术实现,但是银行财务系统中的保存现金通常却被划为领域逻辑,因为它就是我们常说的存款,有明确的业务含义。看到这,似乎大家又有些 Faint了,这里给出一个判断是否是领域逻辑的原则:就是这个逻辑动作是否有明确的业务上的含义,或者说是否是业务相关的,而不仅仅是技术相关的。

只有将技术实现手段从领域问题中剥离才能保证领域本身的精炼,保证程序员可以把精力集中到领域问题本身上来,而不会满脑子都是技术实现手段。

通常刚做系统分析的人会犯一个错误,把眼晴都订在存储领域了,而不是把精力放在业务领域和技术实现更高的层次来看业务需求。常常会认为这个只是改一个数据表字段的值,那个是多加一个表。。。。。

领域的组成:

按照Eric的表述,通常将领域中的组成角色分为以下五种:

实体(Entity):拥有唯一标识的对象。
值对象(Value Object):没有唯一标识的对象。
工厂(Factory):定义创建实体的方法。
仓储(Repository):管理实体的集合并封装其持久化过程。
服务(Service):实现不能指派或封装在一个单一对象上的操作。

 


实体的概念是比较好理解的,这样的例子很多,比如说每一个人都可以看作是一个“与众不同”的实体,我之所以用与众不同这个词是为了强调实体必须是能 够唯一标识出来的,即便是在我们看作长得一模一样的双胞胎,他们也是能更根据一些标识来区分开,比如指纹,可能你会抬杠,要是没有手的残疾人怎么办?那样 我们还可以使用DNA检测,当然,这些都是笑谈了,实际编程的时候,一般是使用一个自增数来作为标识,比如在SQL SERVER数据库中保存实体的时候可以使用自增字段。需要注意的是如果想判断两个实体是否相等,不能根据实体的属性来判断,必须绝对依赖实体的标识,(区别实体间唯一的标识)十年前的你 和现在的你虽然在身高,体重,年龄等众多重要的属性中多或多或少的发生了变化,但你还是你,因为你的DNA不会因为这些属性的变化而变化。这些理解起来似 乎有些哲学的味道了。对于WEB上常见的论坛的贴子,贴子本身这个实体数据的变化,并不会影响它的唯一标识的,需然,它置顶了,加精了,或给删除了的状态,标识这个实体对象的唯一标识它永远不会变化,反过来,通过这个实体对象的唯一标识可以找到这个实体对象的相关数据。

值对象的含义,老实说相对实体来说比较模糊,很多人喜欢把数据传输对象也称为值对象(数据传输对象和我们这里说的值对象是有差别的)让人们对值对象 的理解产生过很多歧义,而且值对象的例子不如实体那么直接。从字面上来理解,值对象没有唯一标识,大多数情况下,值对象是不变的,所以系统不用实时的跟踪 他们,用的时候就实例化一个,不用的时候就销毁,但是,什么时候使用值对象?把哪些属性划为值对象?值对象的作用是什么?这些都是值得考虑的问题。通常来 说,当我们进行领域建模的时候,优先把唯一标识和经常用来检索对象的信息作为实体的属性,而其他信息根据相关性或者划分到其他实体中,或者划分为值对象, 举例来说:一个CMS系统中,对于文章实体而言,文章编号,文章标题等都应该作为文章实体的属性存在,而对于文章有效性期限的开始时间,结束时间两个信息 则应该被放在一个独立的值对象中,这其中,只有开始时间或结束时间,或者开始时间和结束时间同时都存在或不存在,会代表不同的逻辑意义,合理使用值对象, 既有利于屏蔽一些相关逻辑的复杂性,也可以保持实体对象的简洁。再比如,在上传一个video时,通常有一个video的类别,这个类别是整个网站统一维护,用户是没有维护的这个类别的需求,而且相对而言,这个类别发布后它的值相对的稳定,它没有必要有唯一的对象标识,因为它在系统里是唯一的,那么就可以把它设计成值对象,用户需要上传video时,只是把它读出来。

工厂相对与前两者会好理解的多,毕竟从名字上就能体现出它的职责,那就是创建对象。既然是创建对象,那我们直接实例化一个不行么?简单的情况是可以 的,但是工厂往往会带来巨大的好处,简单的说就是屏蔽了创建对象的复杂性。领域创建对象强调的关联,一组相关的对象应该被看作一个整体,对于其中任何对象 的访问也应该从这个整体的“根”开始(通常整体中最重要的实体作为跟),所以复杂的关联必然会使创建过程同样复杂起来,那我们可不可以在“根”实体的构造 函数中完成对象的组装呢,简单的情况可以,复杂的不合适,比如说组装汽车,通常是在工厂里由组装工人和机器人来操作完成,如果我们在“跟”的构造函数里完 成组装,无异与在汽车里配备了组装工人和机器人,这当然是不必要的,汽车一旦组装出厂,就不需要组装工人和机器人了,此时再附带他们是一种累赘。

仓储的概念和一些人常说的数据访问对象(DAO)有些类似,但是并不等同,二者一个很大的不同是仓储有“根”的概念,而数据访问对象往往是按照数据 库的表来划分的。使用仓储主要是为了查询和持久化领域对象(当然待久化不一定非要用数据库来进行持久,也可以用文件写在磁盘,并且在使用时可以反序),而领域对象之间往往会有复杂的聚合关系,为了保证不变量,所以才引入根的概念,对领域对象中某 个子对象的访问必须通过根来导航。这样说可能不易理解,我举一个简单的例子:轿车,轮胎可以看成是一个领域对象的聚合,轿车是这个聚合的根,如果我们想访 问轮胎,必须通过轿车的导航来进行,为什么如此规定,因为轿车和轮胎之间存在一个不变量:一个轿车有四个轮胎,如果允许客户端直接访问轮胎,那么就很难保 证此逻辑不被破坏。在社区开发中,比如某一个group中的讨论主题与讨论主题的回贴,通常我们是进入到某一个group后才看到这个组相关的讨论主题,打开某一个主题后看到与这个主题相关的post,讨论主题与它下面的post的关系就是一个聚合关系,如果post离开了讨论主题,没有任何的意义,它的访问必须先访问讨论主题后看到这个post,这样才知道这个post的具体意义,如果可以在外部访问,其它的page直接访问这个post,有些不知所云。而且也破坏了讨论主题与post对象的关系。

服务这个名词被用过很多次了,但是以前人们说的服务大多是从技术角度而言的,从分层来看属于应用层。一般是诸如注册成功发送一个邮件之类的东西,领 域驱动设计中的服务不是这个范畴的概念,它强调的是实体之间的相互关系,而不是纯粹意义上的技术手段。举一个例子来说:CMS系统里,如果一篇文章被加入 精华,则文章作者的经验值加一。此逻辑中涉及量个实体:文章实体和作者实体。经验值加一的逻辑不管是建立在文章实体里,还是作者实体里都显得冗余,所以有 必要在实体之上在抽象出一个服务层来处理。这里可能有人会问:这样的逻辑我们放到传统意义上的应用层不行么?那样做不能说不行,但是多数情况不好,因为此 逻辑属于领域逻辑,而不是应用逻辑,如果放在应用层,领域逻辑就外泄了,领域层也就成为了摆设,但是也有例外,有时候我们可能一时很难分辨一个逻辑是领域 逻辑还是应用逻辑,这个时候把此逻辑加入到应用层是没有问题的,如果以后发现其作为领域逻辑更合适的话再重构不迟。

写完了回头看看,感觉只能称之为涂鸦心得,领域驱动设计更多要靠自己的体会。