posts - 47, comments - 129, trackbacks - 2, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
    从我们最初接触面向对象思想的时候,我想我们接触到的第一个概念应该就是“”,我们一直在讨论诸如如何设计类、如何实现类等高深的问题,但是我们有没有思索过到底什么叫做“类”,类的本质是什么?。按照大多数的面向对象的书籍中的介绍来看,类就是一个数据结构,封装了数据和操作,对于这样的答案,我估计大家都不会满意。
    那到底什么是类呢?在讨论这个问题之前,我们先探讨一下类的由来。“”在英语对应的单词是“Class”,如果大家翻一翻英语词典就可以查到“Class”的原意是指“种类、把...分类(或分等级)”。Class的概念最早应该是从分类学来的,意思是把对象进行归类(说的可能有些不太准确,欢迎那位高人指正),例如生物学上会根据某一个标准将生物分为动物和植物两大类,然后再根据其它的一些标准将动物又分为鱼类、爬行动物类、两栖动物类等不同的种类,如下图所示:

    说到这里,可能大家会欢呼:原来面向对象的类就是分类,太好了!我最擅长这个了!别高兴的太早,谁知道面向对象的分类标准是什么吗?是生物学的标准,还是能不能爬树的标准?不同的标准,导致分类的结果完全不同,如下图所示:

    假设现在需要要写一个弹涂鱼的类(又名虾虎鱼,英文名为Goby,一种可以爬上陆地并且会上树的鱼类,据说味道极其鲜美,有海上人参之说) ,怎么写?是不是太容易了,看下面的代码,分分钟就搞定了:
 1 '
 2 Public Class Fish
 3 
 4 End Class
 5 
 6 '可爬树的鱼
 7 Public Class ClimbableFish
 8     Inherits Fish
 9 
10 End Class
11 
12 '弹涂鱼
13 Public Class Goby
14     Inherits ClimbableFish
15 
16 End Class
    打完收功,貌似很完美的解决问题,但是这个时候又添加了一个分类标准,能吃的鱼和不能吃的鱼(鲨鱼能吃,俺吃过,味道不咋地,在这里假设鲨鱼不能吃),又该怎么办,Stupid,再写一个“EatableFish”类不就得了,让可爱的弹涂鱼从可以吃的鱼派生,我最喜欢能吃的鱼了!且慢!动手之前我想搞清楚一个问题:EatableFish从那个类派生?从ClimbableFish类派生?难道可以吃的鱼都是会爬树的鱼?从Fish派生,那么是不是说会爬树的鱼都不能吃?这个时候是不是该咒骂微软为什么不在.NET中支持多重继承?算了,还是转投Java阵营算了。旁边的一位兄弟弱弱的来了一句:好像Java也不支持多重继承吧。怎么办?难道我们就没有办法解决这个问题了吗?
    貌似用分类学的搞法搞不定面向对象的类耶,我们错了吗?但是很多教科书上面就是这么说的类的继承是“Is A”(是一个)的关系呀,弹涂鱼是“Is A”能吃的鱼、弹涂鱼“Is A”能爬树的鱼,念起来蛮通顺的嘛。错了!我们都被教科书给误导了!面向对象关注什么?关注的是对象的行为,面向对象是使用行为来对对象进行分类的!在面向对象中派生类为什么能够替换基类(替换原则),不是因为派生类是一个基类,而是因为派生类具有与基类一致的行为,在派生类与基类的行为不一致的情况下派生类仍然是一个基类(如果有人敢否认这个,大家说怎么办?旁边有人喊道:砍死他!),但是这个时候派生类消减了基类的行为,违背了替换原则,这也是恶心设计的由来。所以说,对于面向对象而言我们要关注“Act As”,用“Act As”的标准来对对象进行归类,至于什么“Is A”之类的伪标准统统扔到它姥姥家去。
    旁边有人不干了:你跟我说说属性是什么动作!对呀,属性是个什么动作呢?那么请有如此疑问的朋友仔细的考虑一下,是不是可以将属性考虑为GetXXX和SetXXX的两个方法,至于说字段怎么怎么地的某些兄弟俺就不多说了,回去自个好好想想吧,有些东西是属于开发平台为我们做了很多的工作,只不过我们不知道而已。
   好,问题到这里已经有些眉目了,我们该讨论如何使用“Act As”来对对象进行分类了。
   高手出招了,代码如下:
 1 //可爱的小鱼接口
 2 public interface IFish
 3 {
 4 }
 5 
 6 //可爱的爬树接口
 7 public interface IClimbable
 8 {
 9 }
10 
11 //可以吃接口
12 public interface IEatable
13 {
14 }
15 
16 //弹涂鱼出场了
17 //我要扮演鱼
18 //我要扮演爬树高手
19 //我要扮演可以吃的美味,貌似没有人愿意扮演这个
20 public class Goby : IFish, IClimbable, IEatable
21 {
22 }
    高手!请问我需要怎样表演才能扮演成一条鱼呢?高手愕然。
    看来我们的高手还是没有摆脱“Is A”的荼毒呀!我们不要鱼!我们要的是行为!行为由什么决定的呢?由要用你这个类的地方期望要的行为来决定的,例如我需要一个能够提供游泳行为的对象,你就可以抽象"ISwimable"这个动作(这个单词可能不对),然后寻求实现这个动作的对象就可以了(接口倒置原则)。
    有些朋友可能会有一些疑问,既然是动作,那么动作之间怎么会有继承呢(接口的继承)?例如:
1 //我是一个数据提供源
2 public interface IDataSource : IDisposable
3 {
4 }
5 
    仔细想想,这个是继承吗?是“Is A”吗?不是!不知道大家玩过拳皇或者其它的格斗游戏没有,要知道分别连续按键是可以组合出一个大招的,在某些情况下,对象的使用者或者理论一点的说法消费者,需要是对象同时提供上述的两种行为,不过分吧。软件设计的时候往往就是这个地方出问题,如果没有分清楚的话,很有可能把本应该拆分的动作当作一套组合拳给打了(接口隔离原则),这也是混乱的开始,重构的原因。
    我们一直以来都从“Is A”的角度来对对象进行归类,但是仔细的想一想,“Is A”的标准是什么?我们怎么样才能判定一个对象“Is A”另外一个对象呢?大家是不是基本靠猜测或者凭经验在做?这也是软件设计一直被当作是一种艺术行为的原因。一下这个图是我的一个观点,请大家参考一下:

    其中箭头表示对象的行为,我们关注的行为是指落在系统范围之内的行为,或者系统关心的行为。
    好了,今天就写到这里吧,以后有时间我会再详细的讨论如何分拆动作,如何设计类的话题。


Feedback

#1楼    回复  引用  查看    

2007-08-01 06:48 by 金色海洋(jyk)      
面向对象,面向行为?
我一直在用面向过程的思路来作程序,哈哈.

#2楼    回复  引用  查看    

2007-08-01 07:15 by 布尔      
有待提高

#3楼    回复  引用  查看    

2007-08-01 07:20 by 小生      
不錯﹐有想法﹗
其實面向對象的思想真的很廣泛
在不斷地實踐和思索中才能有很深的見解
那几本教科書和那几個乏善可陳的詞匯就稱為"面向對象"實在是太淺了...
園子里討論這個的很少
支持樓主繼續

#4楼    回复  引用    

2007-08-01 08:21 by sigmazel [未注册用户]
嗯,很不错,希望这样的讨论更多。

分类因人而异,因视角的角度和广度而异,我个人觉得没有必要非要区分行为角度和种属角度,人类语言和计算机语言有时面对复杂的现实世界表现力是很无力的。

#5楼    回复  引用  查看    

2007-08-01 08:32 by temptation      
受益
:)

#6楼    回复  引用  查看    

2007-08-01 08:42 by 浮云      
我认为与是否能吃,是和吃它的动物有关,所以能吃这个属性不应该属于鱼。
按目前的面相对象的方法来处理自然界的事物欠缺的还比较多。

#7楼    回复  引用    

2007-08-01 08:46 by chenger [未注册用户]
如果我没理解错的话,这不就是duck typing吗,关注对象的行为而不是其类型。这种思想在dynamic language里用得很多很灵活

#8楼    回复  引用    

2007-08-01 09:01 by zhou [未注册用户]
期待下篇

#9楼    回复  引用  查看    

2007-08-01 09:04 by Terry Sun      
都是经验之谈了

期待你的下一篇文章

#10楼    回复  引用    

2007-08-01 09:08 by lovebanyi [未注册用户]
颠覆传统。
好象没有吧。。本来就是这个样子的啊

#11楼    回复  引用  查看    

2007-08-01 09:17 by Anders Liu      
嗯。。。建议你在多想想再继续写。

比如这个:public class Goby : IFish, IClimbable, IEatable
我认为这样比较好:public class Goby : Fish, IClimbable, IEatable

看到区别了么?

#12楼 [楼主]   回复  引用  查看    

2007-08-01 09:19 by 我是程序员      
@lovebanyi
知音,绝对是知音,我们拜把子吧

#13楼    回复  引用  查看    

2007-08-01 09:31 by 土星的狗狗      
喜欢啊~想要借用一下并在我们的团队里传发~

#14楼    回复  引用  查看    

2007-08-01 09:35 by 笑煞天      
不错,期待你的续篇!!!

#15楼    回复  引用  查看    

2007-08-01 09:43 by Maple      
关注你的文章,期待下一篇。今天知道了会爬树的鱼,:-)

#16楼    回复  引用    

2007-08-01 09:55 by nirvana_li [未注册用户]
建议博主和大家先看看《敏捷软件开发(原则,模式与实践)》中关于LSP和IS-A的讲述。

IS-A是关于行为的。
基于契约设计
在单元测试中指定契约
启发式规则和习惯用法

#17楼 [楼主]   回复  引用  查看    

2007-08-01 09:56 by 我是程序员      
@土星的狗狗
没有问题,注明出处就可以了

#18楼 [楼主]   回复  引用  查看    

2007-08-01 09:57 by 我是程序员      
@nirvana_li
对,但是大所处情况下我们都误解了“Is A”的实际含义,所以我干脆抛弃的“Is A”的说法,这个说法非常容易引起误解,并且在绝大多数情况下甚至某些教科书中都是错误的。

#19楼    回复  引用  查看    

2007-08-01 10:23 by wanghualiang      
很佩服楼主的发散型思维。但是远远还没到颠覆传统的地步。
这里谈谈我的观点,
面向对象设计时完全从接口来描述对象本身的特性是不是有问题。
从鱼是不是可吃应该只能作为其一个属性来辨识,
Class Fish
{
public bool IsEatable;
}
当客户想吃这条鱼的时候,IsEatable=true;如果是河豚的话就是 False了。
当然有许多种不确定的因素,在可吃不可吃之间。那我们应该
[Flags]
Enum Eattype
{

DeliciousEate,//美味
Distasteful,
Barbed,
.....
}

Class Fish
{
public Eattype eattype;
}

#20楼    回复  引用  查看    

2007-08-01 10:38 by 刘荣华      
@Anders Liu
同意这条,呵呵

#21楼    回复  引用  查看    

2007-08-01 10:52 by 菌哥      
行为就是方法!

#22楼    回复  引用  查看    

2007-08-01 10:54 by 周奔驰      
弱弱的问一句,和传统的is a /has a 有什么区别吗

#23楼    回复  引用    

2007-08-01 11:01 by Dove [未注册用户]
写得非常好呀,受益!

#24楼    回复  引用  查看    

2007-08-01 11:04 by OK_008      
写的不错,路过学习。

#25楼    回复  引用  查看    

2007-08-01 11:22 by henry      
只有最后那个图才体现出楼主所说的行为分类的好处,在程序觉得协助分类比较合适.如果对象只关注自己本身那行为分类没有多大意义.

#26楼    回复  引用  查看    

2007-08-01 11:25 by netflu      
你没有颠覆传统,只是正朝着正确的面向对象方象前进

#27楼    回复  引用    

2007-08-01 12:01 by zlvy [未注册用户]

楼主怀疑与探索的精神值得赞赏!
另外:wanghualiang 的看法也很好!

#28楼    回复  引用  查看    

2007-08-01 13:25 by Anytao      
@wanghualiang
@Anders Liu
@刘荣华
LZ的见解很独特,值得我们对面向对象设计思想花点儿时间思索。上面三位的观点也没有问题。
其实,不管是IS-A、Act-AS还是Can-DO,面对的都是不同的业务情况,不可一概而论,不能单传的以Act-AS的角度,也不能以IS-A一统江湖。镞的概念多一点建议为IS-A,例如public class Fish: Animal; 而行为的概念更突出时,多一点Can-Do,例如public class Goby: ISwimable。
封装属性还是方法;继承状态还是实现行为;抽象类多态还使接口多态,关键还得着眼于具体的情况。

#29楼    回复  引用    

2007-08-01 13:50 by MAXCHEN [未注册用户]
object-oriented, 面向对象, 模拟现实世界 生物系统的行为

目前的OOL,在遗传方面,还可以吧,

但变异的处理不好,使之局限性颇大

#30楼    回复  引用  查看    

2007-08-01 15:14 by 针式个人知识库管理      
做个记号!

#31楼    回复  引用    

2007-08-01 16:58 by hellws [未注册用户]
简直可笑致级,纯粹瞎扯淡

#32楼    回复  引用  查看    

2007-08-01 20:15 by 暗香浮动      
soa?

#33楼    回复  引用  查看    

2007-08-01 23:46 by thh      
教科书真是害人不浅啊!强烈建议把介绍的部分换成设计原则!

#34楼    回复  引用    

2007-08-02 08:58 by 医药网 [未注册用户]
??????????????

#35楼    回复  引用    

2007-08-02 20:18 by 空明流转的马甲 [未注册用户]
实际设计中,楼主的思维方式并不是如同楼主所述的那么罕见。
如果我们只关注鱼怎么吃,恐怕没人会为鱼会不会爬树什么的来生拉硬造一个类出来吧。

#36楼    回复  引用    

2007-08-02 23:54 by 路过 [未注册用户]
有点拘泥于设计了。其实够用就好了

#37楼    回复  引用  查看    

2007-08-03 12:53 by Cure      
没发现要颠覆哪些东西

#38楼    回复  引用  查看    

2007-08-08 19:36 by 丁学      
本来以为自己面向对象了,然后不停的优化自己的代码,最后发现有点面向过程了……

#39楼    回复  引用    

2007-08-09 13:13 by dennis [未注册用户]
颠覆?只是回归罢了
分清楚type跟class的区别 ,object属于某个type,依据的是行为,而不是它的class
而静态OO语言如(java ,c#,c++)的OO模型是静态的,java说一切都是object,其实呢
它想说的是一切都是class,无论什么东西都用class包装一把,可class!=type,因此
静态oo语言中充斥着依据class进行消息分派的代码。反观,
动态OO语言(比如smalltalk,python,ruby等)中的duck typing正是最初的OO思想,我想看看某个对象是否能是属于鸭子这个类型,那么我们就看看它能能像鸭子那嘎嘎叫:obj.respond_to?:duck_call。
而OO的引入又可以追溯到Lisp中
引入局部状态变量来模拟时间变化的计算对象,我们能模块化地构造系统,同时也带来了
赋值引入的副作用。Remmeber that:Classes Aren't Types.

#40楼    回复  引用    

2007-08-09 21:05 by sp1234 [未注册用户]
依我看这种想法导致的结果就是“一个方法一个接口”,最后干脆完全回到结构化设计了事。

#41楼    回复  引用    

2007-08-20 18:36 by xx [未注册用户]
那请问 面向对象中为什么还要有类的继承呢?????

#42楼    回复  引用  查看    

2008-07-24 01:00 by 傲然林      
起床再看喔。

#43楼    回复  引用    

2008-08-11 17:47 by JavaOO [未注册用户]
面向对象应该是把问题的数据用归类的思想分析清楚,然后争对功能做责任分配的方法处理数据.
这与高中证明几何题目是类似的,充分理解条件,充分理解结论,找一个方法搞定.

其实在项目设计中主要设计流程.这是原滋原味的面向过程,但这种以过程为主的设计在设计过程中忽略数据条件,导致过程设计的不确定性与反复修改.然后才有先数据分类分析,然后根据目标数据责任分配的OOAD.

楼主分析的不错,但脱离软件实际应用来谈OO,总是缺点什么,教科书就是这样的毒药.毒害一带人,而这代人还自称OO高手!

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-08-01 08:51 编辑过


相关链接: