- 浏览: 413668 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
liyuanhoa_:
...
struts2.0中struts.xml配置文件详解 -
chenmingde:
...
Velocity应用(一) -
weizhikai_ai:
第二十六,当一个线程进入一个对象的一个synchronized ...
Java常见面试题(含答案) -
Aurora_lr:
...
Spring宠物商店学习笔记(一) - -
zs911zs:
all copy from http://www.iteye ...
Mule入门文档
很久以前大家就关于这个方面有很多讨论了。前两天我又挖了一个坑来集思广益,非常感谢没有把我的帖子投为新手帖的同志。我不是在装傻,只是想让大家跳出自己的立场,从根本的价值出发来考虑问题。之前有很多讨论,都是在讨论我又发明了一种新方法可以让领域模型充血啦,等等之类的。当提出一个解决方案的时候,一定要有明确的问题。那么领域模型的价值是什么?为什么没有被广泛应用,其困境在哪里?
价值
数据,一定是数据。做企业系统,最核心的东西一定是数据。关于数据,人们有许多需求,但是最根本的一点就是,数据要是对的。在关系数据库的上下文下,为了保证数据是对的,我们有外键,我们有COLUMN的数据类型,我们有主键,我们有constraint,我们有很多很多。但是很多时候还不够,一堆数据在业务上是不是合法的,超过了上述的检查方法的能力范畴。这个时候,以DBA为中心的思考就会导致:我作为DBA,管理这些数据,如果数据出了问题,那就是我的责任了。所以我必须要阻止愚蠢的事情,而我显然是最知道什么是正确数据的人,所以你们(程序员)要访问我的数据,就必须通过我的存储过程。
这种方式显然遇到了问题。问题是很多方面的,有人员素质问题,有工具支持问题。更重要的是,虽然存储过程起到了防火墙的作用,阻挡了外界可能的对数据一致性的破坏,但是其内部却是脆弱的。数据对于包裹它的存储过程都是开放的,写存储过程A的人,可能对数据的假设与写存储过程B的人对数据假设是不一致的。两个人必然只有一个是正确的,但是从数据出发找到修改它的地方并不容易,从而给数据的质量埋下了隐患。
存储过程的问题,就是面向过程的代表。面向对象的主要特征,封装就是为了解决这个问题发明的。把数据放置于对象内部,要修改对象所封装的数据,就必须通过对象所提供的外在行为。有如下图所示。
回到数据的正确性这个问题。程序员不同于DBA,给出的解决方案是领域模型。其实领域模型,只是面向对象的另外一个名字而已。通过把数据封装在领域模型的内部,我们就可以限制模型的使用者对数据的修改,什么值是对的,什么样的值是不对的。具体列出来有:
构造函数
可以确保在创建的时候已经有了所有的必填项
Java代码
public class Person {
public Person(String firstName, String lastName) {
...
}
..
}
public class Person {
public Person(String firstName, String lastName) {
...
}
..
}
无Set方法
不能任意的改变值,必须通过特定的合法性检验
Java代码
public class Publication {
private State currentState;
public State publish(Channel to) {
...
}
...
}
public class Publication {
private State currentState;
public State publish(Channel to) {
...
}
...
}
关联
可以保证外键,以及强制约束两个表之间数据的关系
Java代码
public class Cargo {
public void attachItinerary(final Itinerary itinerary) {
// Decouple the old itinerary from this cargo
itinerary().setCargo(null);
// Couple this cargo and the new itinerary
this.itinerary = itinerary;
this.itinerary.setCargo(this);
}
...
}
public class Cargo {
public void attachItinerary(final Itinerary itinerary) {
// Decouple the old itinerary from this cargo
itinerary().setCargo(null);
// Couple this cargo and the new itinerary
this.itinerary = itinerary;
this.itinerary.setCargo(this);
}
...
}
一致性
冗余字段的同步更新得到强制
Java代码
public class ShoppingChart {
private List<OrderItem> items;
private int sum; //冗余字段
public void dropIntoChart(OrderItem newItem) {
sum += newItem.sum();
items.add(newItem);
}
...
}
public class ShoppingChart {
private List<OrderItem> items;
private int sum; //冗余字段
public void dropIntoChart(OrderItem newItem) {
sum += newItem.sum();
items.add(newItem);
}
...
}
当然,面向对象不光是封装一个特性,它还有继承和多态。所以作为面向对象的另外一个名字,它自然也有继承和多态这个好处。具体到程序里就是
枚举值
不要通过对枚举值的判断来决定程序的路径
Java代码
// 过去
public void publish(ChannelType channelType, Publication publication) {
if (channelType.equals(ChannelType.RETUERS)) {
...
} else if (channelType.equals(ChannelType.BLOOMBERG)) {
...
} ...
}
//现在
public interface Channel {
void publish(Publication publication);
...
}
public class RetuersChannel implements Channel {
...
}
public class BloombergChannel implements Channel {
...
}
// 过去
public void publish(ChannelType channelType, Publication publication) {
if (channelType.equals(ChannelType.RETUERS)) {
...
} else if (channelType.equals(ChannelType.BLOOMBERG)) {
...
} ...
}
//现在
public interface Channel {
void publish(Publication publication);
...
}
public class RetuersChannel implements Channel {
...
}
public class BloombergChannel implements Channel {
...
}
数据的含义
另外一个好处是,对数据的访问被集中起来了。所以,从数据出发,很容易发现计算出值并修改它的地方。这就方便了我们去理解数据的含义。数据本身是没有任何意义的,数据只有被使用才有意义。只有理解了数据的上下文的含义,才能编写更多的行为去操作数据。在写新的行为的时候,我们必然要参考过去的行为是怎么理解数据的含义的。这个查找的过程越容易,越有助于我们写出正确的逻辑,也越有助于我们发现过去已经写过一样的行为了,那我就不用写了,也就是所谓的复用。
所以,理论上来说,面向对象或者说领域模型是非常适合我们的日常的企业信息系统开发工作的。但是,实践中,却遇到了很多问题。
困境
框架的约束
如Robin所言
robin 写道
如果你用的是Spring,没啥说的,必须贫血,你想充血也充不起来;
如果你用的是RoR,也没啥说的,直接充血,你想贫血也未必贫得下来;
这就是一个基本事实。Spring作者也坦言(Rod Johnson, JAOO, 2006),Spring的编程模型基本上是EJB的延续。从架构和分层的角度,它们是一脉相承的。这种分层的架构决定了,行为在Service里,数据在Entity里。这种做法成为“最佳实现”,不是偶然的,是框架设计给你的必然结果。矛盾的集中体现在于Entity无法被注入(当然你可以注入,但是这是不推荐的做法)。Spring后来尝试修复这个问题,引入了AspectJ来做Entity的注入。不过仍然不是主流,因为框架的阻碍只是一个小问题,更大的问题在于这个Java的OOP实现本身就有问题。
语言的约束
在很多的讨论中,反对“充血”领域模型的同志都提到。把逻辑集中到领域模型中,会造成类的膨胀。在我个人的实践中,近千行的Entity类定义也是有的。而且这些行为往往不稳定,往往流程高度相关,复用程度并不是想象的那么高。
原因是因为,数据并没有所谓的内在行为。你不用它,它就没有行为。所以数据有什么行为,其实是使用者赋予的。而同样的数据往往会在不同的场合(context)下被使用。正如这位同志所言:
coolnight 写道
我们的系统有很多模块组成, 各模块基本上通过数据库来共享信息。
主要的模块大致有: 核心系统(非web), 网站、 bbs、 网站后台,核心系统后台,BI, 推广员等等
原来的打算是写个rich domain model供各模块来使用,以便代码重用减轻个模块开发的工作量
一个简单的例子, User 原有 changePassword, getFriends, addFriend ... 等等方法撇开配置以及获取User对象的复杂性不谈, 实际开发中发现, 这些东西重用的地方太少了,对网站来说很好的rich domain model, 在网站后台里面就麻烦,很多方法在那里根本就是对后台开发人员的干扰,而很多方法对核心系统、BI等等根本就毫无用处。
所以上面所画的图就得改一下了:
同样,很多同志也发现了这个问题,并给出了解决方案
Quake Wang 写道
用mixin实现领域模型是最简洁的做法, 但是受到Java语言的限制,得用大量mark性质的interface和composite模式,反而会让整个结构变得复杂。
相比C#的namespace或ruby自定义dsl(acts_as_xxx),在java玩领域模型需要更多的功力,推荐一下Rickard Öberg的qi4j,它将composite oriented发挥到了极致:
http://www.qi4j.org/
ray_linn 写道
我建议用C#的扩展方法,或者Java用mimix(不知道打错没有),Domain还是POJO,方法在适当的时候“黏合”(C#通过 namespace引用)到POJO上,让POJO “充血”,而在充当VO,DTO的时候,POJO又可以“放血”回到“贫血的”状态上。
这个问题基本上是暴露了Java的OOP实现的缺陷。C#的Partial Class, Extension Method和Ruby的Mixin就是对这种“一个class定义一切”的做法的改进,允许行为被分片定义,而不是集中定义在一个文件中。
现状
很多同学都谈到现状不是完全的没有领域模型,而是所谓的贫血的领域模型。之所以会这样,就是前面所说的困境。正如很多朋友所说的,这未必是一件坏事,比如可以避免核心的domain过度膨胀。不过也未必是一件好事,理由我能想到这么几点:
越俎代庖
这是我们的DAO经常干的事情。比如我们有两个domain,Publication(一份文档)和Distribution(一次分发)。它们两者之间是聚合关系,也就是distribution必然属于一份publication。如果是重写的领域模型,我们可以通过publication来控制对distribution数据的添加。比如
Java代码
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
但是一旦有了DistributionDao,就不一样了。
Java代码
distributionDao.save(new Distribution(somePublication));
distributionDao.save(new Distribution(somePublication));
哈哈,啥控制都管不了我了吧。很多时候Dao就是这么无法无天的。如果说domain model是数据的防火墙,那么dao就是顶级黑客。由于Entity不能注入等实现和宗教信仰上的限制,往往导致了service对DAO的滥用,就会造成上面这样的by-pass domain model的情况。
舍近求远
明明我有一个contact
Java代码
public class Contact {
private List<ContactNote> contactNotes;
...
}
public class Contact {
private List<ContactNote> contactNotes;
...
}
但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
Java代码
from ContactNote where contacted = thisContact and ...
from ContactNote where contacted = thisContact and ...
我有一个contact就应该能够让我
Java代码
contact.allMeeetingContactNotes();
contact.allMeeetingContactNotes();
但是不行,你得
Java代码
contactNoteDao.findMeetingContactNoteByContact(thisContact);
contactNoteDao.findMeetingContactNoteByContact(thisContact);
舍近求远也
职责混杂
根据我理解的Service层的现状是:有一部分是作为domain model对外的接口,提供了事务安全等服务,真正是一个“service”对web层提供服务;有的呢,则是当作一些可重用的行为被注入到其他的service之中;而有的呢,则纯粹是一些utils。当然,不过可能是我用的不对吧,或者理解有误。
距离啊距离
数据和行为距离很远。造成了不容易理解数据含义,因为你光看entity压根不知道它会被怎么用。漏掉一些servie中细微的用法,就可能会造成很大的bug。
同时这也更加让可重用的逻辑更难被发现,从一定程度上鼓励了大家各自发明,一份逻辑写n遍,还n遍都不一样。。。
当然啦,这个问题没有想象的那么大,毕竟贫血的领域模型,还是鼓励大家把“只和这个对象,不和外部接口有关的”的逻辑放到对象本身的。问题的大小,取决于团队的稳定性,素质和职业操守。
术语
如nihongye同学所说,xxxService不是domain language。当然啦,讨论这种问题最终是仁者见仁智者见智的。
解困
你真的要解困吗?其实你未必被困住了。你可能根本不需要领域模型,特别是在Java/Spring这种实现下,难以实现。正如很多同志所言,我用贫血模型用得很好。那就行,自己好,就是真的好。但是有朝一日,觉得贫血模型不再适合你了,不妨去了解了解qi4j(如果你不想换语言),或者投入rails的怀抱吧。
大小: 7.9 KB
大小: 12.8 KB
声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
轻松优化数据管理环境
IBM Rational软件开发高峰论坛9月揭幕
在繁琐中挣扎还是简化自主管理?
返回顶楼 最后修改:2008-11-28
ronghao 等级:
性别:
文章: 205
积分: 419
来自: 北京
发表时间:2008-11-28 引用 收藏 我试着小结一下:
领域模型的价值:
1、从业务使用的角度保证数据的准确性(因为贴近业务,所以比单纯的数据库约束更完善)
2、行为与数据一致,不再对数据显式的判断(内置)
3、在领域模型里集中对数据的访问,方便理解数据的含义。(不再被分隔到多个service里)
返回顶楼 0 0
downpour 等级:
性别:
文章: 827
积分: 1748
发表时间:2008-11-28 引用 收藏 理论啊理论,哲学啊哲学,什么时候才能回到实践的道路上来?
简单谈谈你所说的现状:
taowen 写道
越俎代庖
这是我们的DAO经常干的事情。比如我们有两个domain,Publication(一份文档)和Distribution(一次分发)。它们两者之间是聚合关系,也就是distribution必然属于一份publication。如果是重写的领域模型,我们可以通过publication来控制对distribution数据的添加。比如
Java代码
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
但是一旦有了DistributionDao,就不一样了。
Java代码
distributionDao.save(new Distribution(somePublication));
distributionDao.save(new Distribution(somePublication));
哈哈,啥控制都管不了我了吧。很多时候Dao就是这么无法无天的。如果说domain model是数据的防火墙,那么dao就是顶级黑客。由于Entity不能注入等实现和宗教信仰上的限制,往往导致了service对DAO的滥用,就会造成上面这样的by-pass domain model的情况。
请问,isDeleted()方法由谁来实现?是框架嘛?我想无论如何你都无法避免框架对你的帮助吧。
事实上,多数情况下,如果你想通过publication来控制对distribution的数据添加,我们会这样写代码:
Java代码
// this could be in Publication
public void addDistribution(Distribution distribution) {
this.distributions.add(distribution);
distribution.setPublication(this);
}
// this could be in Service
publication.addDistribution(new Distribution(somePublication));
publicationDao.saveOrUpdate(publication); // this could be ignore using hibernate
// this could be in Publication
public void addDistribution(Distribution distribution) {
this.distributions.add(distribution);
distribution.setPublication(this);
}
// this could be in Service
publication.addDistribution(new Distribution(somePublication));
publicationDao.saveOrUpdate(publication); // this could be ignore using hibernate
至于说到你数据层面的业务校验,这些可能会在Service中完成,因为他们需要有更多的外部依赖。这样写你是不是觉得也可以?你认为越俎代庖了嘛?
taowen 写道
舍近求远
明明我有一个contact
Java代码
public class Contact {
private List<ContactNote> contactNotes;
...
}
public class Contact {
private List<ContactNote> contactNotes;
...
}
但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
Java代码
from ContactNote where contacted = thisContact and ...
from ContactNote where contacted = thisContact and ...
我有一个contact就应该能够让我
Java代码
contact.allMeeetingContactNotes();
contact.allMeeetingContactNotes();
但是不行,你得
Java代码
contactNoteDao.findMeetingContactNoteByContact(thisContact);
contactNoteDao.findMeetingContactNoteByContact(thisContact);
舍近求远也
这点更加是无从谈起了,为什么不写个字段,把hibernate的where用上?简单实用。事实上很多项目我们都是这么使用的。
返回顶楼 8 0
taowen 等级:
性别:
文章: 386
积分: 1484
来自: 北京
发表时间:2008-11-28 引用 收藏 downpour 写道
请问,isDeleted()方法由谁来实现?是框架嘛?我想无论如何你都无法避免框架对你的帮助吧。
publication是软删除的。所以isDeleted只是判断自身的一个属性是不是true而已。
downpour 写道
至于说到你数据层面的业务校验,这些可能会在Service中完成,因为他们需要有更多的外部依赖。这样写你是不是觉得也可以?你认为越俎代庖了嘛?
当然是可以的,只是约束数据的地方不在单单是在Domain Model上,还包括Service。别人要修改数据的时候,通不通过你的service就是职业操守的问题了。service给数据添加的约束不是强制的,而domain model可以添加强制的约束。就是这么一个问题。当然啦,你大可以说这是理论。我也说过了,贫血不贫血不是问题,适不适合自己才是问题。
downpour 写道
这点更加是无从谈起了,为什么不写个字段,把hibernate的where用上?简单实用。事实上很多项目我们都是这么使用的。
你是指formula property吗?
返回顶楼 0 0
gamix 等级: 初级会员
文章: 1
积分: 0
发表时间:2008-11-28 引用 收藏 引用
舍近求远
明明我有一个contact
Java代码 复制代码
1. public class Contact {
2. private List<ContactNote> contactNotes;
3. ...
4. }
public class Contact {
private List<ContactNote> contactNotes;
...
}
但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
Java代码 复制代码
1. from ContactNote where contacted = thisContact and ...
from ContactNote where contacted = thisContact and ...
我有一个contact就应该能够让我
Java代码 复制代码
1. contact.allMeeetingContactNotes();
contact.allMeeetingContactNotes();
但是不行,你得
Java代码 复制代码
1. contactNoteDao.findMeetingContactNoteByContact(thisContact);
contactNoteDao.findMeetingContactNoteByContact(thisContact);
舍近求远也
我记得DDD上说如果是聚合的话,访问聚合的任何元素都应该从根开始,所以这个例子中Contact得到ContactNote我觉得并不是舍近求远,而是封装的体现。
另外,关于.net中使用partial的方式来定义类,我确实有考虑过,但实际使用中特别依赖于团队规范,否则很容易让开发人员找不到北。扩展方法也是同理,而且扩展方法我认为是不到万不得已都不应该去用的东西。
返回顶楼 0 0
taowen 等级:
性别:
文章: 386
积分: 1484
来自: 北京
发表时间:2008-11-28 引用 收藏 gamix 写道
我记得DDD上说如果是聚合的话,访问聚合的任何元素都应该从根开始,所以这个例子中Contact得到ContactNote我觉得并不是舍近求远,而是封装的体现。
老兄,你把我的意思看反了……
gamix 写道
另外,关于.net中使用partial的方式来定义类,我确实有考虑过,但实际使用中特别依赖于团队规范,否则很容易让开发人员找不到北。扩展方法也是同理,而且扩展方法我认为是不到万不得已都不应该去用的东西。
这是一个很好的问题。AOP,Extension Method, Mixin都会导致对行为的隐式聚合。也就是在没有一个地方告诉我,这段代码都有啥玩意实现了它,修改了它。qi4j做法是最终有一个Composite接口声明了所有的实现(Mixin)和修改者(Modifier)。我个人也比较倾向于把行为分开来写,最后一个地方再把所有的东西集中在一起。
返回顶楼 0 0
robbin 等级:
性别:
文章: 5376
积分: 1935
来自: 上海
发表时间:2008-11-28 引用 收藏 我来说几句吧:
第一、DAO层和TransactionScript层是邪恶的!
我们在2004年一直跨度到2007年讨论来讨论去,其实都有一个隐含的前提条件:你的领域模型终究无法脱离对DAO层的依赖,以及需要TransactionScript层的包裹。而这样一来,领域模型的通用性、可移植性、业务逻辑的表达能力基本全部丧失,沦为框架限制下的奴隶。
而我们看看现在Java领域的技术进步,JPA已经普及,EJB3的隐含事务管理,甚至连Spring也可以简化成@Transactional,现在已经是我们可以去掉DAO和TransactionScript的时代了。
第二、Seam在消除了DAO层和TransactionScript以后,领域模型的感觉已经呼之欲出
这个不用多说,大家自己去看Seam文档好了。我唯一想强调的是entity bean和business bean分开有没有必要性,我的看法是有必要!这还是和Java本身语言的限制有关系:
1、Java语法表达能力有限,entity bean又不得不弄一大堆getter/setter方法,都放在一个class里面,代码的可阅读性非常差
2、business bean的很多业务逻辑方法需要容器环境,不像Rails的model可以直接mixin进来
3、Java做为目前最主流的工业语言,开发团队都是大规模编码协作的,你都放在一个class里面,团队协作会遇到很大的麻烦(事实上RoR现在也有这样的问题,但是RoR开发效率高,往往不需要那么大规模的开发团队)
3、领域模型不同类别的业务逻辑可以很容易的分到几个不同的business bean里面,这样对团队协作的好处很大。
第三、不考虑框架限制,All-in-One的领域模型好不好?
比方说RoR的model就是All-in-One的,但是一旦出现可以抽象出来,比较通用的业务逻辑,我们还是会把这些逻辑抽出来,单独实现,然后再mixin回去。
所以最终我对这个问题的总结就是:
一、只要技术框架能够实现,尽量使用领域模型
二、无论Java还是Ruby,必须消灭DAO和TransactionScript
三、领域模型不必All-in-One,Java可以分割为 1个entity bean和几个business bean,而Ruby可以分割为1个model和几个mixin的module。
返回顶楼 0 0
downpour 等级:
性别:
文章: 827
积分: 1748
发表时间:2008-11-28 引用 收藏 taowen 写道
当然是可以的,只是约束数据的地方不在单单是在Domain Model上,还包括Service。别人要修改数据的时候,通不通过你的service就是职业操守的问题了。service给数据添加的约束不是强制的,而domain model可以添加强制的约束。就是这么一个问题。当然啦,你大可以说这是理论。我也说过了,贫血不贫血不是问题,适不适合自己才是问题。
我明白了,你所谈的哲学问题或者碰到的困境,还是在于程序员素质本身。而你所要解决的问题在于,如果从语法或者自然的角度就能把类似约束或者通用逻辑的问题解决,那是再好不过的事情。
我想这个初衷是没问题的。不过貌似在Java这个层面上,我们需要花费更多的代价来完成,或许借助框架,或许借助其他手段。可能有时候就需要在这些框架技术和哲学之间做出选择了。
taowen 写道
downpour 写道
这点更加是无从谈起了,为什么不写个字段,把hibernate的where用上?简单实用。事实上很多项目我们都是这么使用的。
你是指formula property吗?
formula可以做,不过要设置好lazy,否则会有性能问题。同时,一对多关联关系上也是可以设置查询条件的。
返回顶楼 0 0
downpour 等级:
性别:
文章: 827
积分: 1748
发表时间:2008-11-28 引用 收藏 robbin 写道
一、只要技术框架能够实现,尽量使用领域模型
二、无论Java还是Ruby,必须消灭DAO和TransactionScript
三、领域模型不必All-in-One,Java可以分割为 1个entity bean和几个business bean,而Ruby可以分割为1个model和几个mixin的module。
第一点基本同意。不过在Java世界,有时候不得不在技术框架和使用哲学上做出balance。
第二点持保留意见,目前Dao要完全消除的可能性不大,JBoss Seam提供了范例,不过必须有容器做环境。但是我认为Dao可以是非常非常薄的一层,甚至可以不需要有实现。这一点Robbin曾经介绍过一个框架,用Annotation实现,论坛上也有人曾经用Annotation在Spring上实现过。
第三点完全同意。
返回顶楼 0 0
yananay 等级:
文章: 426
积分: 414
发表时间:2008-11-28 引用 收藏 我倒不觉得 DAO 是邪恶的。
我更加趋向于存在一个DAO层,或许不因该叫 DAO,应该叫 PAO(Persistence Access Object):持久访问对象的层。
因为在程序设计的早期,应该具有的概念是持久层,而不是“数据库”,数据库仅仅是持久的一个方式。
而这个层应该是面向接口的,至于实现,99%的情况应该都是数据库,但是也可能有的操作是保存到一个特殊的xml,有的操作是保存到远程的一个服务器上。即使都是数据库,也可能使用 ibatis 或者 hibernate 等不同的方式来实现。
所以大概的流程就是:
service -> domain model -> PAO (PAO 的实现就根据实际情况了)
当然,这里的 PAO 只是一个名词,沿用熟悉的称呼,叫它为 DAO 也是一样。
价值
数据,一定是数据。做企业系统,最核心的东西一定是数据。关于数据,人们有许多需求,但是最根本的一点就是,数据要是对的。在关系数据库的上下文下,为了保证数据是对的,我们有外键,我们有COLUMN的数据类型,我们有主键,我们有constraint,我们有很多很多。但是很多时候还不够,一堆数据在业务上是不是合法的,超过了上述的检查方法的能力范畴。这个时候,以DBA为中心的思考就会导致:我作为DBA,管理这些数据,如果数据出了问题,那就是我的责任了。所以我必须要阻止愚蠢的事情,而我显然是最知道什么是正确数据的人,所以你们(程序员)要访问我的数据,就必须通过我的存储过程。
这种方式显然遇到了问题。问题是很多方面的,有人员素质问题,有工具支持问题。更重要的是,虽然存储过程起到了防火墙的作用,阻挡了外界可能的对数据一致性的破坏,但是其内部却是脆弱的。数据对于包裹它的存储过程都是开放的,写存储过程A的人,可能对数据的假设与写存储过程B的人对数据假设是不一致的。两个人必然只有一个是正确的,但是从数据出发找到修改它的地方并不容易,从而给数据的质量埋下了隐患。
存储过程的问题,就是面向过程的代表。面向对象的主要特征,封装就是为了解决这个问题发明的。把数据放置于对象内部,要修改对象所封装的数据,就必须通过对象所提供的外在行为。有如下图所示。
回到数据的正确性这个问题。程序员不同于DBA,给出的解决方案是领域模型。其实领域模型,只是面向对象的另外一个名字而已。通过把数据封装在领域模型的内部,我们就可以限制模型的使用者对数据的修改,什么值是对的,什么样的值是不对的。具体列出来有:
构造函数
可以确保在创建的时候已经有了所有的必填项
Java代码
public class Person {
public Person(String firstName, String lastName) {
...
}
..
}
public class Person {
public Person(String firstName, String lastName) {
...
}
..
}
无Set方法
不能任意的改变值,必须通过特定的合法性检验
Java代码
public class Publication {
private State currentState;
public State publish(Channel to) {
...
}
...
}
public class Publication {
private State currentState;
public State publish(Channel to) {
...
}
...
}
关联
可以保证外键,以及强制约束两个表之间数据的关系
Java代码
public class Cargo {
public void attachItinerary(final Itinerary itinerary) {
// Decouple the old itinerary from this cargo
itinerary().setCargo(null);
// Couple this cargo and the new itinerary
this.itinerary = itinerary;
this.itinerary.setCargo(this);
}
...
}
public class Cargo {
public void attachItinerary(final Itinerary itinerary) {
// Decouple the old itinerary from this cargo
itinerary().setCargo(null);
// Couple this cargo and the new itinerary
this.itinerary = itinerary;
this.itinerary.setCargo(this);
}
...
}
一致性
冗余字段的同步更新得到强制
Java代码
public class ShoppingChart {
private List<OrderItem> items;
private int sum; //冗余字段
public void dropIntoChart(OrderItem newItem) {
sum += newItem.sum();
items.add(newItem);
}
...
}
public class ShoppingChart {
private List<OrderItem> items;
private int sum; //冗余字段
public void dropIntoChart(OrderItem newItem) {
sum += newItem.sum();
items.add(newItem);
}
...
}
当然,面向对象不光是封装一个特性,它还有继承和多态。所以作为面向对象的另外一个名字,它自然也有继承和多态这个好处。具体到程序里就是
枚举值
不要通过对枚举值的判断来决定程序的路径
Java代码
// 过去
public void publish(ChannelType channelType, Publication publication) {
if (channelType.equals(ChannelType.RETUERS)) {
...
} else if (channelType.equals(ChannelType.BLOOMBERG)) {
...
} ...
}
//现在
public interface Channel {
void publish(Publication publication);
...
}
public class RetuersChannel implements Channel {
...
}
public class BloombergChannel implements Channel {
...
}
// 过去
public void publish(ChannelType channelType, Publication publication) {
if (channelType.equals(ChannelType.RETUERS)) {
...
} else if (channelType.equals(ChannelType.BLOOMBERG)) {
...
} ...
}
//现在
public interface Channel {
void publish(Publication publication);
...
}
public class RetuersChannel implements Channel {
...
}
public class BloombergChannel implements Channel {
...
}
数据的含义
另外一个好处是,对数据的访问被集中起来了。所以,从数据出发,很容易发现计算出值并修改它的地方。这就方便了我们去理解数据的含义。数据本身是没有任何意义的,数据只有被使用才有意义。只有理解了数据的上下文的含义,才能编写更多的行为去操作数据。在写新的行为的时候,我们必然要参考过去的行为是怎么理解数据的含义的。这个查找的过程越容易,越有助于我们写出正确的逻辑,也越有助于我们发现过去已经写过一样的行为了,那我就不用写了,也就是所谓的复用。
所以,理论上来说,面向对象或者说领域模型是非常适合我们的日常的企业信息系统开发工作的。但是,实践中,却遇到了很多问题。
困境
框架的约束
如Robin所言
robin 写道
如果你用的是Spring,没啥说的,必须贫血,你想充血也充不起来;
如果你用的是RoR,也没啥说的,直接充血,你想贫血也未必贫得下来;
这就是一个基本事实。Spring作者也坦言(Rod Johnson, JAOO, 2006),Spring的编程模型基本上是EJB的延续。从架构和分层的角度,它们是一脉相承的。这种分层的架构决定了,行为在Service里,数据在Entity里。这种做法成为“最佳实现”,不是偶然的,是框架设计给你的必然结果。矛盾的集中体现在于Entity无法被注入(当然你可以注入,但是这是不推荐的做法)。Spring后来尝试修复这个问题,引入了AspectJ来做Entity的注入。不过仍然不是主流,因为框架的阻碍只是一个小问题,更大的问题在于这个Java的OOP实现本身就有问题。
语言的约束
在很多的讨论中,反对“充血”领域模型的同志都提到。把逻辑集中到领域模型中,会造成类的膨胀。在我个人的实践中,近千行的Entity类定义也是有的。而且这些行为往往不稳定,往往流程高度相关,复用程度并不是想象的那么高。
原因是因为,数据并没有所谓的内在行为。你不用它,它就没有行为。所以数据有什么行为,其实是使用者赋予的。而同样的数据往往会在不同的场合(context)下被使用。正如这位同志所言:
coolnight 写道
我们的系统有很多模块组成, 各模块基本上通过数据库来共享信息。
主要的模块大致有: 核心系统(非web), 网站、 bbs、 网站后台,核心系统后台,BI, 推广员等等
原来的打算是写个rich domain model供各模块来使用,以便代码重用减轻个模块开发的工作量
一个简单的例子, User 原有 changePassword, getFriends, addFriend ... 等等方法撇开配置以及获取User对象的复杂性不谈, 实际开发中发现, 这些东西重用的地方太少了,对网站来说很好的rich domain model, 在网站后台里面就麻烦,很多方法在那里根本就是对后台开发人员的干扰,而很多方法对核心系统、BI等等根本就毫无用处。
所以上面所画的图就得改一下了:
同样,很多同志也发现了这个问题,并给出了解决方案
Quake Wang 写道
用mixin实现领域模型是最简洁的做法, 但是受到Java语言的限制,得用大量mark性质的interface和composite模式,反而会让整个结构变得复杂。
相比C#的namespace或ruby自定义dsl(acts_as_xxx),在java玩领域模型需要更多的功力,推荐一下Rickard Öberg的qi4j,它将composite oriented发挥到了极致:
http://www.qi4j.org/
ray_linn 写道
我建议用C#的扩展方法,或者Java用mimix(不知道打错没有),Domain还是POJO,方法在适当的时候“黏合”(C#通过 namespace引用)到POJO上,让POJO “充血”,而在充当VO,DTO的时候,POJO又可以“放血”回到“贫血的”状态上。
这个问题基本上是暴露了Java的OOP实现的缺陷。C#的Partial Class, Extension Method和Ruby的Mixin就是对这种“一个class定义一切”的做法的改进,允许行为被分片定义,而不是集中定义在一个文件中。
现状
很多同学都谈到现状不是完全的没有领域模型,而是所谓的贫血的领域模型。之所以会这样,就是前面所说的困境。正如很多朋友所说的,这未必是一件坏事,比如可以避免核心的domain过度膨胀。不过也未必是一件好事,理由我能想到这么几点:
越俎代庖
这是我们的DAO经常干的事情。比如我们有两个domain,Publication(一份文档)和Distribution(一次分发)。它们两者之间是聚合关系,也就是distribution必然属于一份publication。如果是重写的领域模型,我们可以通过publication来控制对distribution数据的添加。比如
Java代码
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
但是一旦有了DistributionDao,就不一样了。
Java代码
distributionDao.save(new Distribution(somePublication));
distributionDao.save(new Distribution(somePublication));
哈哈,啥控制都管不了我了吧。很多时候Dao就是这么无法无天的。如果说domain model是数据的防火墙,那么dao就是顶级黑客。由于Entity不能注入等实现和宗教信仰上的限制,往往导致了service对DAO的滥用,就会造成上面这样的by-pass domain model的情况。
舍近求远
明明我有一个contact
Java代码
public class Contact {
private List<ContactNote> contactNotes;
...
}
public class Contact {
private List<ContactNote> contactNotes;
...
}
但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
Java代码
from ContactNote where contacted = thisContact and ...
from ContactNote where contacted = thisContact and ...
我有一个contact就应该能够让我
Java代码
contact.allMeeetingContactNotes();
contact.allMeeetingContactNotes();
但是不行,你得
Java代码
contactNoteDao.findMeetingContactNoteByContact(thisContact);
contactNoteDao.findMeetingContactNoteByContact(thisContact);
舍近求远也
职责混杂
根据我理解的Service层的现状是:有一部分是作为domain model对外的接口,提供了事务安全等服务,真正是一个“service”对web层提供服务;有的呢,则是当作一些可重用的行为被注入到其他的service之中;而有的呢,则纯粹是一些utils。当然,不过可能是我用的不对吧,或者理解有误。
距离啊距离
数据和行为距离很远。造成了不容易理解数据含义,因为你光看entity压根不知道它会被怎么用。漏掉一些servie中细微的用法,就可能会造成很大的bug。
同时这也更加让可重用的逻辑更难被发现,从一定程度上鼓励了大家各自发明,一份逻辑写n遍,还n遍都不一样。。。
当然啦,这个问题没有想象的那么大,毕竟贫血的领域模型,还是鼓励大家把“只和这个对象,不和外部接口有关的”的逻辑放到对象本身的。问题的大小,取决于团队的稳定性,素质和职业操守。
术语
如nihongye同学所说,xxxService不是domain language。当然啦,讨论这种问题最终是仁者见仁智者见智的。
解困
你真的要解困吗?其实你未必被困住了。你可能根本不需要领域模型,特别是在Java/Spring这种实现下,难以实现。正如很多同志所言,我用贫血模型用得很好。那就行,自己好,就是真的好。但是有朝一日,觉得贫血模型不再适合你了,不妨去了解了解qi4j(如果你不想换语言),或者投入rails的怀抱吧。
大小: 7.9 KB
大小: 12.8 KB
声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
轻松优化数据管理环境
IBM Rational软件开发高峰论坛9月揭幕
在繁琐中挣扎还是简化自主管理?
返回顶楼 最后修改:2008-11-28
ronghao 等级:
性别:
文章: 205
积分: 419
来自: 北京
发表时间:2008-11-28 引用 收藏 我试着小结一下:
领域模型的价值:
1、从业务使用的角度保证数据的准确性(因为贴近业务,所以比单纯的数据库约束更完善)
2、行为与数据一致,不再对数据显式的判断(内置)
3、在领域模型里集中对数据的访问,方便理解数据的含义。(不再被分隔到多个service里)
返回顶楼 0 0
downpour 等级:
性别:
文章: 827
积分: 1748
发表时间:2008-11-28 引用 收藏 理论啊理论,哲学啊哲学,什么时候才能回到实践的道路上来?
简单谈谈你所说的现状:
taowen 写道
越俎代庖
这是我们的DAO经常干的事情。比如我们有两个domain,Publication(一份文档)和Distribution(一次分发)。它们两者之间是聚合关系,也就是distribution必然属于一份publication。如果是重写的领域模型,我们可以通过publication来控制对distribution数据的添加。比如
Java代码
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
public void distribute() {
if (isDeleted()) {
throw new InvalidDistributionException();
}
distributions.add(new Distribution(this));
lastDistributionDate = new Date();
}
但是一旦有了DistributionDao,就不一样了。
Java代码
distributionDao.save(new Distribution(somePublication));
distributionDao.save(new Distribution(somePublication));
哈哈,啥控制都管不了我了吧。很多时候Dao就是这么无法无天的。如果说domain model是数据的防火墙,那么dao就是顶级黑客。由于Entity不能注入等实现和宗教信仰上的限制,往往导致了service对DAO的滥用,就会造成上面这样的by-pass domain model的情况。
请问,isDeleted()方法由谁来实现?是框架嘛?我想无论如何你都无法避免框架对你的帮助吧。
事实上,多数情况下,如果你想通过publication来控制对distribution的数据添加,我们会这样写代码:
Java代码
// this could be in Publication
public void addDistribution(Distribution distribution) {
this.distributions.add(distribution);
distribution.setPublication(this);
}
// this could be in Service
publication.addDistribution(new Distribution(somePublication));
publicationDao.saveOrUpdate(publication); // this could be ignore using hibernate
// this could be in Publication
public void addDistribution(Distribution distribution) {
this.distributions.add(distribution);
distribution.setPublication(this);
}
// this could be in Service
publication.addDistribution(new Distribution(somePublication));
publicationDao.saveOrUpdate(publication); // this could be ignore using hibernate
至于说到你数据层面的业务校验,这些可能会在Service中完成,因为他们需要有更多的外部依赖。这样写你是不是觉得也可以?你认为越俎代庖了嘛?
taowen 写道
舍近求远
明明我有一个contact
Java代码
public class Contact {
private List<ContactNote> contactNotes;
...
}
public class Contact {
private List<ContactNote> contactNotes;
...
}
但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
Java代码
from ContactNote where contacted = thisContact and ...
from ContactNote where contacted = thisContact and ...
我有一个contact就应该能够让我
Java代码
contact.allMeeetingContactNotes();
contact.allMeeetingContactNotes();
但是不行,你得
Java代码
contactNoteDao.findMeetingContactNoteByContact(thisContact);
contactNoteDao.findMeetingContactNoteByContact(thisContact);
舍近求远也
这点更加是无从谈起了,为什么不写个字段,把hibernate的where用上?简单实用。事实上很多项目我们都是这么使用的。
返回顶楼 8 0
taowen 等级:
性别:
文章: 386
积分: 1484
来自: 北京
发表时间:2008-11-28 引用 收藏 downpour 写道
请问,isDeleted()方法由谁来实现?是框架嘛?我想无论如何你都无法避免框架对你的帮助吧。
publication是软删除的。所以isDeleted只是判断自身的一个属性是不是true而已。
downpour 写道
至于说到你数据层面的业务校验,这些可能会在Service中完成,因为他们需要有更多的外部依赖。这样写你是不是觉得也可以?你认为越俎代庖了嘛?
当然是可以的,只是约束数据的地方不在单单是在Domain Model上,还包括Service。别人要修改数据的时候,通不通过你的service就是职业操守的问题了。service给数据添加的约束不是强制的,而domain model可以添加强制的约束。就是这么一个问题。当然啦,你大可以说这是理论。我也说过了,贫血不贫血不是问题,适不适合自己才是问题。
downpour 写道
这点更加是无从谈起了,为什么不写个字段,把hibernate的where用上?简单实用。事实上很多项目我们都是这么使用的。
你是指formula property吗?
返回顶楼 0 0
gamix 等级: 初级会员
文章: 1
积分: 0
发表时间:2008-11-28 引用 收藏 引用
舍近求远
明明我有一个contact
Java代码 复制代码
1. public class Contact {
2. private List<ContactNote> contactNotes;
3. ...
4. }
public class Contact {
private List<ContactNote> contactNotes;
...
}
但是我不能直接去取我的contactNotes(性能啊性能,hibernate会load all啊)。所以我得写个查询
Java代码 复制代码
1. from ContactNote where contacted = thisContact and ...
from ContactNote where contacted = thisContact and ...
我有一个contact就应该能够让我
Java代码 复制代码
1. contact.allMeeetingContactNotes();
contact.allMeeetingContactNotes();
但是不行,你得
Java代码 复制代码
1. contactNoteDao.findMeetingContactNoteByContact(thisContact);
contactNoteDao.findMeetingContactNoteByContact(thisContact);
舍近求远也
我记得DDD上说如果是聚合的话,访问聚合的任何元素都应该从根开始,所以这个例子中Contact得到ContactNote我觉得并不是舍近求远,而是封装的体现。
另外,关于.net中使用partial的方式来定义类,我确实有考虑过,但实际使用中特别依赖于团队规范,否则很容易让开发人员找不到北。扩展方法也是同理,而且扩展方法我认为是不到万不得已都不应该去用的东西。
返回顶楼 0 0
taowen 等级:
性别:
文章: 386
积分: 1484
来自: 北京
发表时间:2008-11-28 引用 收藏 gamix 写道
我记得DDD上说如果是聚合的话,访问聚合的任何元素都应该从根开始,所以这个例子中Contact得到ContactNote我觉得并不是舍近求远,而是封装的体现。
老兄,你把我的意思看反了……
gamix 写道
另外,关于.net中使用partial的方式来定义类,我确实有考虑过,但实际使用中特别依赖于团队规范,否则很容易让开发人员找不到北。扩展方法也是同理,而且扩展方法我认为是不到万不得已都不应该去用的东西。
这是一个很好的问题。AOP,Extension Method, Mixin都会导致对行为的隐式聚合。也就是在没有一个地方告诉我,这段代码都有啥玩意实现了它,修改了它。qi4j做法是最终有一个Composite接口声明了所有的实现(Mixin)和修改者(Modifier)。我个人也比较倾向于把行为分开来写,最后一个地方再把所有的东西集中在一起。
返回顶楼 0 0
robbin 等级:
性别:
文章: 5376
积分: 1935
来自: 上海
发表时间:2008-11-28 引用 收藏 我来说几句吧:
第一、DAO层和TransactionScript层是邪恶的!
我们在2004年一直跨度到2007年讨论来讨论去,其实都有一个隐含的前提条件:你的领域模型终究无法脱离对DAO层的依赖,以及需要TransactionScript层的包裹。而这样一来,领域模型的通用性、可移植性、业务逻辑的表达能力基本全部丧失,沦为框架限制下的奴隶。
而我们看看现在Java领域的技术进步,JPA已经普及,EJB3的隐含事务管理,甚至连Spring也可以简化成@Transactional,现在已经是我们可以去掉DAO和TransactionScript的时代了。
第二、Seam在消除了DAO层和TransactionScript以后,领域模型的感觉已经呼之欲出
这个不用多说,大家自己去看Seam文档好了。我唯一想强调的是entity bean和business bean分开有没有必要性,我的看法是有必要!这还是和Java本身语言的限制有关系:
1、Java语法表达能力有限,entity bean又不得不弄一大堆getter/setter方法,都放在一个class里面,代码的可阅读性非常差
2、business bean的很多业务逻辑方法需要容器环境,不像Rails的model可以直接mixin进来
3、Java做为目前最主流的工业语言,开发团队都是大规模编码协作的,你都放在一个class里面,团队协作会遇到很大的麻烦(事实上RoR现在也有这样的问题,但是RoR开发效率高,往往不需要那么大规模的开发团队)
3、领域模型不同类别的业务逻辑可以很容易的分到几个不同的business bean里面,这样对团队协作的好处很大。
第三、不考虑框架限制,All-in-One的领域模型好不好?
比方说RoR的model就是All-in-One的,但是一旦出现可以抽象出来,比较通用的业务逻辑,我们还是会把这些逻辑抽出来,单独实现,然后再mixin回去。
所以最终我对这个问题的总结就是:
一、只要技术框架能够实现,尽量使用领域模型
二、无论Java还是Ruby,必须消灭DAO和TransactionScript
三、领域模型不必All-in-One,Java可以分割为 1个entity bean和几个business bean,而Ruby可以分割为1个model和几个mixin的module。
返回顶楼 0 0
downpour 等级:
性别:
文章: 827
积分: 1748
发表时间:2008-11-28 引用 收藏 taowen 写道
当然是可以的,只是约束数据的地方不在单单是在Domain Model上,还包括Service。别人要修改数据的时候,通不通过你的service就是职业操守的问题了。service给数据添加的约束不是强制的,而domain model可以添加强制的约束。就是这么一个问题。当然啦,你大可以说这是理论。我也说过了,贫血不贫血不是问题,适不适合自己才是问题。
我明白了,你所谈的哲学问题或者碰到的困境,还是在于程序员素质本身。而你所要解决的问题在于,如果从语法或者自然的角度就能把类似约束或者通用逻辑的问题解决,那是再好不过的事情。
我想这个初衷是没问题的。不过貌似在Java这个层面上,我们需要花费更多的代价来完成,或许借助框架,或许借助其他手段。可能有时候就需要在这些框架技术和哲学之间做出选择了。
taowen 写道
downpour 写道
这点更加是无从谈起了,为什么不写个字段,把hibernate的where用上?简单实用。事实上很多项目我们都是这么使用的。
你是指formula property吗?
formula可以做,不过要设置好lazy,否则会有性能问题。同时,一对多关联关系上也是可以设置查询条件的。
返回顶楼 0 0
downpour 等级:
性别:
文章: 827
积分: 1748
发表时间:2008-11-28 引用 收藏 robbin 写道
一、只要技术框架能够实现,尽量使用领域模型
二、无论Java还是Ruby,必须消灭DAO和TransactionScript
三、领域模型不必All-in-One,Java可以分割为 1个entity bean和几个business bean,而Ruby可以分割为1个model和几个mixin的module。
第一点基本同意。不过在Java世界,有时候不得不在技术框架和使用哲学上做出balance。
第二点持保留意见,目前Dao要完全消除的可能性不大,JBoss Seam提供了范例,不过必须有容器做环境。但是我认为Dao可以是非常非常薄的一层,甚至可以不需要有实现。这一点Robbin曾经介绍过一个框架,用Annotation实现,论坛上也有人曾经用Annotation在Spring上实现过。
第三点完全同意。
返回顶楼 0 0
yananay 等级:
文章: 426
积分: 414
发表时间:2008-11-28 引用 收藏 我倒不觉得 DAO 是邪恶的。
我更加趋向于存在一个DAO层,或许不因该叫 DAO,应该叫 PAO(Persistence Access Object):持久访问对象的层。
因为在程序设计的早期,应该具有的概念是持久层,而不是“数据库”,数据库仅仅是持久的一个方式。
而这个层应该是面向接口的,至于实现,99%的情况应该都是数据库,但是也可能有的操作是保存到一个特殊的xml,有的操作是保存到远程的一个服务器上。即使都是数据库,也可能使用 ibatis 或者 hibernate 等不同的方式来实现。
所以大概的流程就是:
service -> domain model -> PAO (PAO 的实现就根据实际情况了)
当然,这里的 PAO 只是一个名词,沿用熟悉的称呼,叫它为 DAO 也是一样。
发表评论
-
利用动态代理的 Java 验证
2008-12-18 17:41 821从业务对象实现中去耦 ... -
Java 理论与实践: 用动态代理进行修饰
2008-12-18 17:41 778动态代理是构建 Decorator 和 Adapter 的方便 ... -
分布式软件系统
2008-12-18 15:49 1443分布式软件系统(Distributed Software Sy ... -
SOA实践 -- 使用IoC和AOP重构SOA应用
2008-12-18 15:35 964在本文中,作者通过一个Web Service访问的实例,具体描 ... -
SOA 案例研究:SOA 设计
2008-12-17 15:00 1045http://www.ibm.com/developerwor ... -
RUP
2008-12-12 16:53 690RUP(Rational Unified Proces ... -
什么是敏捷开发?
2008-12-05 11:17 2427敏捷开发(agile development)是一种以人为核心 ... -
深入理解敏捷开发的常见九大误区
2008-12-05 11:16 1470任人、开发者和用户应 ... -
对领域模型实现的总结性观点
2008-12-04 17:14 1125陶文发起的对领域模型 ... -
OO设计原则----依赖倒置原则(DIP)
2008-12-02 01:52 1079这是一个类与类之间调用规则,术语上解释就是对于组合之间的规范。 ... -
工厂模式与OO设计原则
2008-12-02 01:50 1128如果把创建看作一个职 ... -
设计模式之--动态代理
2008-11-26 18:03 1606动态代理类是一个在运行时由开发人员所指定的一列接口的实现。动态 ... -
Facade外观模式
2008-11-26 16:06 977GOF《设计模式》一书对Facade模式是这样描述的: ... -
Adapter适配器模式
2008-11-26 16:05 828GOF《设计模式》一书对Adapter模式是这样描述的: ... -
Strategy策略模式
2008-11-26 16:05 1067GOF《设计模式》一书对Strategy模式是这样描述的: ... -
Bridge桥接模式
2008-11-26 16:04 899设计模式》一书对Bridge是这样描述的: 将抽象与其实现解 ... -
Abstract Factory抽象工厂模式
2008-11-26 16:03 744GOF《设计模式》一书对Abstract Factory模式是 ... -
Decorator装饰模式
2008-11-26 16:01 868《设计模式》一书对Decorator是这样描述的: 动态地给 ... -
Observer观察者模式
2008-11-26 15:59 915《设计模式》一书对Observer是这样描述的: 定义对象间的 ... -
Template Method模式
2008-11-26 15:58 970factory模式(包括简单工厂和抽象工厂),Strategy ...
相关推荐
财务困境预测模型是财务管理领域的重要研究方向,其核心目的是通过对上市公司的财务数据进行分析,提前发现企业可能面临的财务困境风险,并给出相应的预警。财务困境又称财务危机,通常表现为公司出现严重的资产折现...
《财务困境-MertonDD模型》是金融领域一个重要的理论模型,它主要用于评估企业违约的风险,通过对企业的市场价值和负债的分析,预测企业可能面临的财务困境。Merton模型由诺贝尔经济学奖得主罗伯特·默顿提出,是...
"囚徒困境"模型是博弈论中的一个经典案例,由Tucker在1950年提出,用于描述在非合作博弈环境下,个体最优...在实际应用中,这个模型广泛用于解释社会、经济和政治领域的许多现象,如市场竞争、环境保护和国际关系等。
因此,联邦学习既保护了数据的隐私性,又通过合作提升了模型的性能,实现了数据的隐私保护和价值利用的双重目标。 在金融领域,联邦学习的落地实践尤为关键。金融数据通常非常敏感,涉及大量个人隐私和机构机密。...
在IT领域,尤其是在复杂系统建模与仿真方面,囚徒困境博弈(Prisoner's Dilemma)是一个重要的理论模型,它常被用来研究合作与背叛的行为动态。此模型结合了数学、经济学和计算机科学,而Matlab作为一种强大的数值...
"2业价值研究院-人工智能行业应对AI数据困境:恰当的数据集成方法、治理和工具"这篇报告深入探讨了这一主题,旨在为AI从业者提供宝贵的指导。 首先,报告强调了数据集成的重要性。在AI系统中,数据是模型训练的基础...
囚徒困境是博弈论中的一个经典模型,用来研究合作与背叛的决策问题。两个囚犯面临两个选择:合作(双方都沉默)或背叛(一方揭发另一方)。如果两人都合作,他们会得到较小的惩罚;如果两人都背叛,他们都会受到较大...
4. **理论与应用价值**:此研究拓宽了企业信息化研究领域,从技术角度转向经济管理角度,探索如何将企业信息化与电子商务有效结合,构建理论框架和决策模型。其应用前景包括提高企业信息化投资效率,加速电子商务的...
雪堆模型与囚徒困境不同的是,遇到背叛者时合作者的收益高于双方相互背叛的收益。因此,一个人的最佳策略取决于对手的策略:如果对手选择合作,他的最佳策略是背叛;反过来,如果对手选择背叛,那么他的最佳策略是...
在识别和预防财务困境与财务欺诈方面,现代技术扮演着关键角色。数据分析和人工智能技术能帮助企业及早发现财务困境的预警信号,通过大数据分析,找出可能导致财务困境的模式和趋势。区块链技术的应用则可以提高财务...
总之,MM模型是公司财务管理领域的一个基础理论,它揭示了资本结构与企业价值之间的关系,并在理论与实践中持续发挥着重要作用,尽管其假设可能与现实世界存在一定的差距。通过对MM模型的理解和应用,企业和管理者...
本文档主要探讨了在财务风险预警领域,传统的“Z-Score”模型与人工智能视角下新兴的财务风险预警模型之间的比较。文中首先概述了财务风险预警的重要性,并指出了在大数据、云计算、移动技术和人工智能(大智移云)...
基于修正Jones盈余管理模型的财务危机预警研究报告主要探讨了修正Jones模型在财务危机预警领域的应用可能性及其对财务危机预警模型预测效果的影响。该研究首先引入了盈余管理理论,通过单变量分析初步证实了修正...
最终定价可能受到市场情况和谈判实力的影响,而投资决策则基于价值与价格的比较。 现值和贴现公式是理解公司估值的核心概念,任何资产的价值都等于其未来现金流的现值之和。这强调了现金流的重要性,因为它直接影响...
总的来说,财务模型和公司估值是金融领域中必不可少的工具,用于量化公司的经济价值,为投资决策提供依据。无论是投资银行还是投资管理,都需要掌握这些方法,以便做出明智的交易决策,并在复杂多变的金融市场中保持...
当企业面临财务困境时,其负债的价值可能会降低至零,而股权价值则会受到影响。因此,通过评估企业的负债与股权之间的关系,可以估算出企业破产的可能性。 在公司财务预警分析中,奥特曼模型的应用主要体现在以下几...
ChatGPT 是一个由 OpenAI 团队开发的语言模型,具有广泛的应用场景与潜在的巨大价值。本文将就 ChatGPT 的应用场景与潜在价值进行详细的讨论。 1. 客服场景:ChatGPT 模型可以通过学习大量的客户服务对话,掌握如何...
该模型特别针对第三方物流服务的特点,结合近年来我国成品油价格频繁上涨的现状,分析了当前物流企业在服务费用调整方面所面临的困境,并提出了解决方案。 首先,成品油价格的频繁波动对物流企业运作成本的影响极大...
研究发现,这些因子对于预测上市公司是否陷入财务困境具有重要价值。 在构建预测模型时,文章采用了基于极大似然估计的向前筛选策略,并通过Logistic回归分析,发现了五个重要的因子变量F1、F2、F3、F4、F5。这些...