谈谈book.Save()到底OO还是不够OO

在之前争论贫血还是充血的时候,有Tx提出这样一个观点book.Save()用起来很怪,有人认为这样子的用法不够OO,因为保存书不是书所具有的行为,而是书籍管理员:BookManager来发出比较合理。这里来说说我的看法,还是OO,继续接砖。

首先是我的看法,book.Save() 是不是符合OO的精神,关键是看怎么用。打个比方。如果是EditBook.aspx.cs中出现了book.Save()那么我觉得是不符合OO设计的思想的,因为保存书的行为确实不是书本身发出的。但是,如果有一个用户类User,在User里有个UpdateBook的方法,在这个方法里出现了book.Save(),那么我认为这是很OO的,因为修改后保存书的行为确实是用户发出的。但是有人可能会问了,那为什么不直接把book的属性读出来生成Sql去update数据库(也就是BookManager的做法,个人不赞同此做法)呢?很简单,根据OO的封装性原则,Book类的实例对自身状态的改变必须是由自己完成,所以我们需要对持久化book的属性这个改变book状态(未持久化状态到持久化状态的改变),如果是交给BookManager去完成那么就破坏了Book类的封装性,自然也就不OO了。

所以OO与否是看你设计的时候所用的思想是否遵循了OO的原则,而不是但看某行代码,book.Save()能OO也可以不OO,贫血模型不够OO的地方在于将事件序列的发起者交给了Service从而破坏了类本身的封装性。

论证完毕,劈砖大大的欢迎,写得很短,是因为快要下班了。

posted on 2007-09-19 18:07 亚历山大同志 阅读(3182) 评论(41)  编辑 收藏 所属分类: 随笔

评论

#1楼  2007-09-19 18:17 xiao_p      

别的不知道,你的背景换了确实好看了不少!   回复  引用  查看    

#2楼  2007-09-19 18:30 kiler      

确实,以前黑色的背景太伤眼睛了。

顺便说一下,我觉得book.Save()这个很不OO,自己保存自己?,还有book.Save()这个方式将来在保存多个对象时,实现事务比较别扭。   回复  引用  查看    

#3楼  2007-09-19 18:38 金色海洋(jyk)      

面向对象就是讨论book.Save()这一类的东东是不是够OO吗?
我觉得不管够不够,只要能够实现客户的要求就可以了呀。

  回复  引用  查看    

#4楼  2007-09-19 18:57 悟知网境(马甲) [未注册用户]

讨论这个问题等同于讨论C和C++   回复  引用    

#5楼  2007-09-19 19:14 skyweo      

这个问题不应该独立出来考虑吧,俺觉着需要结合你的架构,如果你的架构用这种方式比较高效比较合适比较舒服,那OO不OO倒是其次的,OO的提出并不是让人信仰OO,OO只是一种工具,我们要做的只是在不同的环境下维持相对的平衡。所以我觉得讨论这个问题有点“咬文嚼字”的味道了,呵呵 :)   回复  引用  查看    

#6楼  2007-09-19 19:38 利花科技      

我同意楼上仁兄的观点,对于OO不同的人不同的看法。关键就在于你怎样去理解了。OO理论不是强加于某个人的思想之中的   回复  引用  查看    

#7楼  2007-09-19 19:41 怪怪      

这个问题确实容易变得咬文嚼字. 比如咱们对String的持久化, 总不能也让String自己完成吧, 那么就不是OO了..? 如果把BookManager作为图书管理员或者书架, 书架完成书的收集又有何不可?

其实我的出发点正是想提醒大家放下这种争论.., 哪知道一来二去又开始了...   回复  引用  查看    

#8楼  2007-09-19 20:04 一点看法 [未注册用户]

book.Save()这里算持久化的操作吧?既然是属于持久化的就没什么OO的说法。

根据DDD的理论这个方法是属于repository的范畴,跟对象本身所属的Entity/Value Object无关   回复  引用    

#9楼  2007-09-19 20:08 怪怪      

@一点看法
你个解释我觉得大家应该仔细琢磨一下.., 其实我要是还是原来那个马甲, 我也会这么直接.   回复  引用  查看    

#10楼  2007-09-19 20:54 STS [未注册用户]

book.Save是什么意思, 是把編程里的數據保存了, 還是描述現實中把書放回書架??

沒搞清編程和現實的區別,根本不能談OO.

  回复  引用    

#11楼  2007-09-19 20:55 SPARON      

我觉得这样比较好吧user.save(BOOK),用户的一个行为保存,要保存什么呢?保存书。看起来一目了然。。。   回复  引用  查看    

#12楼  2007-09-19 21:15 deerchao      

同意STS的意见.

另外,没有人规定持久化的操作不能由对象自己负责吧?--我倒是觉得只有对象自己最了解自己的数据,所以自己和别的类一起配合实现持久化操作比较合适,至少没有违反"封装"的原则,把数据透明的交给别人看.
  回复  引用  查看    

#13楼  2007-09-19 22:44 lexus      

bookManager.save(Book)   回复  引用  查看    

#14楼  2007-09-20 09:05 henry      

book.Save() 是对的,但这操作应该是由谁来负责就是设计上的问题.
显然这是一个model操作上应该只是Business所见.但可惜的是非常难以做到,因为很多时候model会现在UI层面,从而引发楼主感觉的不合理得出BookManager更合理.在设计上我也比较喜欢BookManager的方式,因为要做到book的行上对谁可见或不可见付出的工作太多了.   回复  引用  查看    

#15楼  2007-09-20 09:21 kiler      

还有一个问题,book.Save() 这种方式如果是水平不高的程序员的调用的话,写出的代码非常乱,什么样的写法都有,使用BookManager会更简洁一点。   回复  引用  查看    

#16楼  2007-09-20 09:32 daizhj      

其实这里说的这个问题太过于局部片面,从软件产品的整体出发才是找到哪个方法到底能放在哪。如果过多讨论这个问题,无异于盲人摸象了,呵呵!   回复  引用  查看    

#17楼  2007-09-20 09:33 蛙蛙池塘      

土点儿不要紧,关键是稳定和性能,BookManager就挺好的。   回复  引用  查看    

#18楼  2007-09-20 09:37 dali [未注册用户]

你们觉得是
Math.Abs(-1)
OO呢, 还是
-1.Abs()   回复  引用    

#19楼  2007-09-20 09:50 kiler      

代码OO不OO无所谓,关键是写出的代码统一风格、简洁、好维护就行,不能搞形式主义,为了OO而OO。   回复  引用  查看    

#20楼  2007-09-20 09:55 Klesh Wong      

"根据OO的封装性原则,Book类的实例对自身状态的改变必须是由自己完成,所以我们需要对持久化book的属性这个改变book状态(未持久化状态到持久化状态的改变),如果是交给BookManager去完成那么就破坏了Book类的封装性,自然也就不OO了。"

那就麻烦了。如果你讲的是对的话,那么序列化岂非也要自已完成?book可以自己save的话。那对应现实中,书应该要会晓得自己跑回书架才比较科学吧?所以你认为书应该自己去跟图书馆、书店这些东东打交道,而user是不用理会这些事情的吗?   回复  引用  查看    

#21楼  2007-09-20 09:56 紫色阴影      

不错,我喜欢book.Save()
不过这个问题争来争去也不会有个结果   回复  引用  查看    

#22楼  2007-09-20 12:14 徐少侠      

所见略同阿!!!
我的博客草稿里就有一份想讨论
对象.save或者 helper.save(对象)的
赫赫
给抢先了

那我就以后丰富了再发了

这里提出我的一个见解

按照亚历山大Tx的思路
在页面上现实书的列表,用户修改特定书的标题后保存更改结果
书这个对象的标题被更改后要保存
这完全是书自己的行为,因此Book.save应该是合理的,

由保存操作是用户引发的,那么亚历山大Tx的说法也是对的
CurrentUser.UpdateBook(Book oBook);
实际代码可能如下
{
..... //省略可能的其他代码
oBook.Save(); //这里调用书对象的保存方法
.....
}

然后就是我的意见了
在book的save方法里,应该调用数据层的东西来做。
这个时候其他Tx的BookSQLHelper可能就有用处了

在Book的save方法里可能是这样的
void Save()
{
BookSQLHelper bh=new BookSQLHelper();
bh.UpdateBook(this);
}

回顾一下
在页面使用了User对象和Book对象,两个业务对象
代表表示层对业务层的引用

在Book对象里使用了BookSQLHelper对象
代表业务层对数据层的引用

在业务稳定的情况下
重写任何一个类的内部实现都不会影响到其他层,也就是说不需要修改代码

回到我开头的想法
究竟使用
对象.save或者 helper.save(对象)

可能 对象.save在业务层更为合适
而 helper.save(对象)在数据层更为合适
  回复  引用  查看    

#23楼  2007-09-20 14:15 镜涛      

呵呵,我觉得OO的出现是为了让大家可以写出优秀的软件,为了OO而OO,却不管对于整个架构的影响,那么是不是就背离了它原来的本意了呢?   回复  引用  查看    

#24楼  2007-09-20 14:34 补丁      

折腾...   回复  引用  查看    

#25楼  2007-09-20 14:37 思粮      

Book.Save()应该是符合OO思想的.
OO世界和现实世界并不完全一致,不能说现实中的书没有保存功能,所以Book类也没有保存功能.实际上,现实世界的book哪需要保存呀.
我记得园子里有个例子,是鱼自己判断能不能被人食用的问题?现实中的鱼当然不知道自己可不可以当作是食物.但在OO世界,于是鱼也就有了这种能力.
OO是将细节隐藏起来,是为了方便我们人类这些笨脑更好的理解问题的实质.并不是简单的对现实世界的复制.
如果是对现实世界的模拟,那么Book.Save当然是错误的.
所以,还要看我们需要解决的问题是什么.不同的问题,对现实世界的抽象的角度也不一样,也就会得出一些不同的解法.   回复  引用  查看    

#26楼  2007-09-20 16:20 Zack2000 [未注册用户]

是Word保存a.doc呢 还是 a.doc保存自己?
显然是前者

什么是对象?
对象是处理数据的程序. 打个比方,
word, excel程序才是OO中的对象, OO对象中的属性或者数据都是临时的,
你把一个word关闭了再起一个, 这已经是2个对象了。
a.doc, b.doc这些我不认为是真正意义上的对象, 或者说他们是ValueObject,
他们的'方法'只是让他们内部的数据保持一致性。而不应该是逻辑。   回复  引用    

#27楼  2007-09-20 16:49 江南白衣      

NND,那Customer.Saver()呢?Order.Save()呢。。。。?我觉得没问题,主要是要做到一致   回复  引用  查看    

#28楼  2007-09-20 17:59 Klesh Wong      

@思粮
作为业务实体,一般情况下会被持久化到数据库没错.但是逻辑上不能就这么片面的理解这就是所有业务实体的宿命吧?难道它就不能被持久化到别的地方去?书有多本,保存的地方也应该有多个,在不同的时间不同的场景下可能被存到不同的地方去,自我保存这种东西不是等于把自己绑死在树上了吗?

还有就是这个世界上根本不存在完全一致的两样东西,你不能以无法一致为由就否定它,OO的本质就在于要尽量把我们的设计往现实趋近.使设计更合理,而且别人看起来也更容易理解.

书的自我保存 / 管理者保存书
哪个看起来更合理?这岂非显而易见?   回复  引用  查看    

#29楼  2007-09-20 18:11 小庄      

BookService.Save() 这样行不?Book类和BookService类都是Book对象!   回复  引用  查看    

#30楼  2007-09-21 11:24 徐少侠      

继续谈我的观点

某个用户将某本书入库了
用户应该有一个将书入库的方法,但是请注意其实书在这里可能仅仅是个实参
因为用户所拥有的是一个入库方法,至于入库什么东西可以不确定
因此该用户的入库方法应该接受一个实现了可入库接口的参数
这么做应该做到很多OO的概念了

然后,在用户的入库方法内,利用多态来实现多种不同实际对象的入库
那么这个时候Book.save的作用将是巨大的
如果book没有save方法,那么就不可能实现用户入库方法的入库对象无关性

重申我的概念
book.save应该用在业务层
BookSQLHelper.save(0booko)应该用在数据层   回复  引用  查看    

#31楼  2007-09-21 22:46 saucer [未注册用户]

同意“思粮"的观点, 不能拘泥于把现实世界与软件世界一一对应

-----如果是EditBook.aspx.cs中出现了book.Save()那么我觉得是不符合OO设计的思想的,因为保存书的行为确实不是书本身发出的。但是,如果有一个用户类User,在User里有个UpdateBook的方法,在这个方法里出现了book.Save(),那么我认为这是很OO的,因为修改后保存书的行为确实是用户发出的。-----

假如把EditBook.aspx.cs设想成一个控制器(Controller),它协调View (EditBook.aspx) 与 Model (Book)之间的行为,那么Controller 调用 book.Save() 是无可非议的吧?

象book.Save()这样的做法在Rails-ActiveRecord里比比皆是,正是因为Book知道自己的细节,也许由它自己来保存才是比较确切的。这个保存不见得一定是存储到数据库里去的,即Book内部实现不见得一定是用SQL。也许Book对象在初始化时,或通过某种Service lookup方法找到系统设置的持久化服务对象(譬如repository),将本身的类型以及数据传给它,然后保存

-----书的自我保存 / 管理者保存书 ---哪个看起来更合理?这岂非显而易见?

不完全同意,在软件世界里,“管理者”完全可能只是个角色,一个Actor,当她触发计算机屏幕上某个保存按钮时,也许是当前屏幕页面的Controller在收集了屏幕的数据后,传给Book model,然后指示Book来保存

void Controller_SaveBook()
{
Book b = new Book();
b.ID = ...;
b.Title = ...;

b.Save();
}   回复  引用    

#32楼  2007-09-21 23:26 bmrxntfj      

理解聚合,仓储。就可以想明白了。   回复  引用  查看    

#33楼  2007-09-22 09:26 Klesh Wong      

@saucer
写程序当然不一定要和现实一一对应了,那不科学.而且也并不是说我是绝对不会用book.Save这种方法的啊.
但现在好像是在说这种设计是不是OO,那么不和现实对应了,OO又是什么?难道说你以为好的就算是OO?没有一个客观一点的准则吗?
一般情况下数据库是存放book的地方,但是你不会反对可以把书存放到不同的地方吧?比如说Cache?比如说序列化到磁盘上?以你的原则来设计的话,是不是要多加两个方法SaveToCache和SaveToDisk?要是你不这么设计的话,是不是只能是以Cache.Save(book), Serializer.Save(book)这种方式去做呢?那这样是不是又成了管理器来保存book的设计呢?如果团队开发的时候,你在数据访问层使用了这种设计觉得很爽,但是对上层的开发人员来说,你的book.Save显然是自私的设计吧?凭什么在大家共用的业务实体上给你划一块私人领地?为什么业务实体要和数据库持久化耦合在一起?这就是你的OO?在你的角度上Save是某一种特定的自我保存都好,那只是你个人的需要,对别人来说Save可能就有不同的意义.   回复  引用  查看    

#34楼  2007-09-22 09:28 Klesh Wong      

@徐少侠
那我要把book"保存"到缓存怎么做?
book.SaveToCache?
Cache.Save(book)?   回复  引用  查看    

#35楼  2007-09-22 10:56 saucer [未注册用户]

我上面的贴子的意思是,亚历山大同志的EditBook.aspx.cs例子也许并不恰当

----OO又是什么?难道说你以为好的就算是OO?没有一个客观一点的准则吗?

我想亚历山大同志的原意是, 不是“这种设计是否是OO”,而是“是否是好OO"的意思,要评价一个OO设计是否是好的OO设计,我想你可以从很多地方读到评定的标准,譬如阎宏的《Java与模式》,Robert Martin的《敏捷软件开发:原则、模式与实践》等等,就不用我赘述了

----一般情况下数据库是存放book的地方,但是你不会反对可以把书存放到不同的地方吧?比如说Cache?比如说序列化到磁盘上? 以你的原则来设计的话,是不是要多加两个方法SaveToCache和SaveToDisk?

如果你有这样的需求的话,你大概应该使用“对可变性的封装原则”,将这些存储机制抽象出来,譬如你也许可以象这样提供一种持久化机制,

interface IPersist
{
void Save(Type t, Object[] data);
}

然后将你的类修改成
class Book
{
//.....
public void Save(IPersist ip);
}

用户点击保存按钮时,你的代码会根据policy生成实现IPersist机制的对象,譬如

void ControllerOrService_SaveBook()
{
IPersist ip = GetPersistMechanismMayBeFromConfiguration();
Book b = new Book();
b.ID = ...;
b.Title = ...;
b.Save(ip);
}

当然你也可以通过Service Locator或Dependency Injection的方式实现,但好像不能把IPersist这样的机制说成是管理器吧

其实在OR/M世界里,大概更多的做法是
IRespository ir;
...
ir.Save(b);

这样Book是单纯的对象(POJO或PONO什么的),跟持久机制更没关系了

----为什么业务实体要和数据库持久化耦合在一起?这就是你的OO?

作为一个开发团队,象这样影响全局的设计,自然是需要有共识才能进行下去的

另外,

----贫血模型不够OO的地方在于将事件序列的发起者交给了Service从而破坏了类本身的封装性

其实,贫血模型在于对象本身内部没有多少业务逻辑,几乎变成了单纯的数据结构,而业余逻辑则散居于服务层或Transaction Script里   回复  引用    

#36楼  2007-09-22 11:46 Klesh Wong      

@saucer
我的意思是说,把你们OO原则讲出来,我们再针对这个设计,然后再继续讨论.

啧,相当学院派的风格呢.但你这种设计和book.Save就完全不一样了吧.加了IPersist自然就解耦了.但是这么设计有必要吗?最终book.save还不是得调用IPersist来实现自我保存,然后IPersist又调用bookmanager或者cache或者serializer来做保存的动作.那可是相当的滑稽啊.

"作为一个开发团队,象这样影响全局的设计,自然是需要有共识才能进行下去的 "

我想既然大家都在讨论了,自然就是假定我们在一个团队里,情况就是现在我们无法达成共识,因为有我这个搅局者存在.哈哈....   回复  引用  查看    

#37楼  2007-09-22 12:07 Klesh Wong      

@saucer
希望你能明白,虽然都是book.save,但你加了个IPersist进去,一切就不同了,和亚历山大同志的想法那是有很大区别的.或许我们可以针对你这个IPersist再另外开篇讨论讨论哦.   回复  引用  查看    

#38楼  2007-09-22 14:28 oxsoft.cn [未注册用户]

怎么用??????????????   回复  引用    

#39楼  2007-09-22 17:00 @Ben [未注册用户]

到底是book.Save()还是BookManage.Save(bookobj),主要处决于book本身.如果根据对象单一职责的话,book只是一个entity.自然book不应该有属于它自已的某些行业,如Save(),Update(),Delete() ,etc.

oo应该是站在整个框架上来思考的问题,而不单纯只是book的问题.如果只有book这个对象,我们有必要说OO吗?跟谁去OO.   回复  引用    

#40楼  2007-10-19 12:04       

把oo吃透。最后变成了怎么方便怎么来。管他book.save()还是manager.save(book).

不过我支持book.select()得到page.哈哈   回复  引用  查看    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接:
所属专题: 关于面向对象的讨论
 




导航

公告

鉴于很多TX投诉黑色背景杀伤眼球,遂换个容易阅读的
<2007年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

统计

与我联系

搜索

 

常用链接

留言簿(28)

我参加的小组

我的标签

随笔分类(82)

随笔档案(82)

相册

朋友的Blog

同事的Blog

最新随笔

积分与排名

最新评论

  • 1. re: 鬼吹灯-漫谈大型网站的架构
  • 图是好画,开发起来并不容易;
  • --三千
  • 2. re: Why .NET Sucks?
  • @亚历山大同志 我认为只要能快速开发出客户需要的服务或应用,快速的为客户实现价值,.net也没什么问题啊。 至于钱钱没有做java的拿的多,一般情况而言两种原因:一、java本身开发难度大、实现繁琐,...
  • --网际浪人
  • 3. re: Why .NET Sucks?
  • --引用-------------------------------------------------- 问天: @Kai.Ma 我找不到开源并且成熟的pop3/imap client,我要求不高...
  • --Ivony...
  • 4. re: Why .NET Sucks?
  • @Kai.Ma我找不到开源并且成熟的pop3/imap client,我要求不高,gb2312/utf 8中文不乱码,能解outlook发的附件就成。这么些年了,.net东西慢慢是有了,但数量、质量无...
  • --问天
  • 5. re: Why .NET Sucks?
  • @问天
    .Net下开源且成熟的东西找找还是蛮多的。:)
  • --Kai.Ma

阅读排行榜

评论排行榜

60天内阅读排行