[200分讨论帖]什么时候该使用继承,以及Liskov替换法则,以及IS-A,以及interface,abstract class....

nirvana_li 2007-08-08 07:00:26
讨论的源动力来自博客园的一篇文章,题目很刺激,叫做
颠覆传统-面向对象的设计思想(序章),下为地址。
http://www.cnblogs.com/zengezenge/archive/2007/07/31/837860.html

博主提出了一个看法说 不能按照教科书上IS-A的方式进行分类,而应该采取颠覆式的Act as 方式。

我的看法是博主无非就在类继承和接口实现的概念上绕来绕去没绕清楚。面向对象的继承就是is-a,而接口实现就是博主所说的act as(我觉得更好的表述是has ... ability/abilites)。

最主要还是希望大家一起讨论一下
1,什么时候应该使用继承
2,Liskov替换法则 是不是就是 IS-A
3,接口和抽象类的区别(这个问题在很多面试题上已经见多了,希望听到更高层次的观点,诸如接口和抽象类如何实现的细节区别就不要说了)

既然是讨论,而且又是200分,只要是观点都给分。
(纯粹jf的我就不给,你把我咋地?)
...全文
662 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
chxzly1981 2007-10-19
  • 打赏
  • 举报
回复
原来面向对象的类就是分类
分类标准
我们不要鱼!我们要的是行为!

以上三句来源于那篇文章,至于哪一行...自己去数吧,反正我认为这是那位作者的主旨所在。
那么,我们来分析一下吧,不过在分析之前我们要知道一些概念:
Object 对象(OO中的第二个O)
Class 类
他们是什么?
“原来面向对象的类就是分类”?真的是这样吗?或者不完全是呢?
我要说的是:无论Object 还是Class 都是抽象出来的,他们不是实体,实体是什么?实体是Object 的实例!或许已经有人晕了,他们会问“Object 不就是实体吗”?真是这样吗?那我要问你,你的Object 的各种描述(属性与方法)哪来的?我们首先要知晓的是,Object 是我们将一个实体(比如你自己)的各种描述(属性与方法)归结为一体而来得,但是,没有人也不可能有人能把一个人(比如你)完完全全的描述的同(你)那个人一样,也就是说Object 与实例(比如你)之间不是相等的!Object 只是实例(比如你)的一种具体的表述,她仍然是抽象的(相对于实体)。
那么类呢?类是分类,是将Object 进行分类。在这里,大家要知道的是,先有实体,然后在他之上抽象出(可能很多人喜欢描述一词)Object ,接着将Object 分(类)归(类)为Class。
接着呢?父类、子类、接口又如何而来呢?既然之前我们一直在抽象(从实体到Object 再到Class),那么那些不同Class内相同的属性与方法不能在分归抽象吗?所有人都知道是可以的。于是被抽象出来的那些属性、方法构成了另外的一些类(接口),也就是父类,于是原来的那些类成了子类。
至于为什么还要抽象?那么当有两个相同功能,但一个需要你做比另一个少的事时,你选哪个?当然,继续抽象的好处远不止如此。我相信“懒人”更适合这一行业,幸运的是,我也很懒。(偷懒?是谁都会的,但不能偷的懒“懒人”是绝对不做的,比如说注释,我可是受害者啊!哭....)。

可能有人会说“你还是没说面向对象的类是不是分类”,我是没说,显而易见的是,抽象的过程中我们不停的在分类,以利于我们的应用,“利于我们的应用”便是标准。对!“分类标准”就是利于应用。
可能许多人不服,凭什么让这个成为标准,而不是像那篇文章中说的,可以有很多。的确,分类的方法或者说分法可以有很多。但是,那么多分法中有多少是符合我们抽象时所确立的目标的呢?我们抽象的,目标是什么?还要我说吗,真正知晓OOA/OOP的人都是知晓的。而这就是OO编程的目标与精髓!

我们不要鱼!我们要的是行为!
对于这句话,让我想起的是AOP(面向切面编程),不可否认的是,这是一种有别于OOP的理念。在现阶段,它仍然只是作为OOP的补充,至于以后是否它真的可以像某些人(那篇文章的作者应该是一个)想的或希望的那样取代OOP,我保留意见,因为AOP真的有点像面向过程编程,呵呵,是不是有人会说“返祖”呢。当然,AOP的好处也是显而易见的,JBOSS大家应该都知道吧,AOP技术运用的典范之一。此外,我是不会反对它的,也不会认为它是在返祖,毕竟它要走得路还很长,光是现今的AOP绝对是无法取代OOP的,或许明天的它便是打开人工智能之门吧,谁知道呢...
chxzly1981 2007-10-19
  • 打赏
  • 举报
回复
up manbaum
不过,由形容词组成的定语可以去掉,那么副词呢?
呵呵,大家都是中国人吧,都应该知道一个句子是有主干的:主语+谓语+宾语。说白了在许多情况下(比如那个文章中)主要指的是由名词+动词(是)+名词组成的语句,那么此外的一切语句成分呢?它们都只是修饰。
manbaum说的很有道理,其实能作为父类成员的无非是那些在一定环境下相对稳定的属性以及方法,他们被抽入父类之后,所有的子类都是继承,甚至方法的实现。说到底,只要他们在我们运用的环境下是不变的。
至于接口,那就是由剩余的可被重用的部分组成,这种重用基本上只存在于抽象层次,否则不就是确定了吗?
manbaum 2007-10-18
  • 打赏
  • 举报
回复
呵呵,楼主提到的文章写的有点搞笑,因为文章作者显然考虑的太浅薄了,他提出的问题是值得考虑的,但并不能推导出他所谓的颠覆。

关于文章里说的按爬树来分类,我们来仔细分析一下。

弹涂鱼 是 鱼。
鲨鱼 是 鱼。

以上两句大家都不认为有问题,显然是IS-A的关系,所以我们说,“弹涂鱼”和“鲨鱼”都应该继承“鱼”。没错!那么继续:

弹涂鱼 是 会爬树的 鱼。
鲨鱼 是 不会爬树的 鱼。

来分析一下:这里的主语是“弹涂鱼”和“鲨鱼”,宾语是“鱼”;“会爬树”以及“不会爬树”是什么?是定语!定语只是修饰,是可以去掉的,去掉了以后,你会发现句子依然是正确的。如果把定语去掉,句子就又回到了前面的那两句。

也许有人会想到这个句子:

鲨鱼 不是 会爬树的鱼。

这里的“会爬树”也是定语啊,可如果去掉了,句子就变成“鲨鱼 不是 鱼”,显然是不正确的。没错,但请注意,我们研究的是IS-A关系,而不是IS-NOT-A关系。两者完全不一样的。应该这么去理解以上这个句子呢?上面的句子实际上隐含了下面这个意思:

“鲨鱼 是 不会爬树的 鱼”或者 “鲨鱼 是 会爬树的 非鱼”

通过对NOT的分配,我们又回到了IS-A关系。现在,我们又可以做“去定语”的游戏了,上面这句就变成:

“鲨鱼 是 鱼”或者 “鲨鱼 是 非鱼”

现在我们可以说,如果用自然语言做模板,IS-A关系关注的是主语和宾语之间的关系。如果IS-A关系成立,使用继承就是正确的!

那么什么时候用接口呢?就是处理这里的定语。定语作为一种修饰,是附加在中心语上的。也就是我们在java里常见的,继承某个类,实现一大堆接口。
小五五 2007-10-18
  • 打赏
  • 举报
回复
留个记号
不接分,看看高人高见
胡矣 2007-10-18
  • 打赏
  • 举报
回复
拿BLOG中的例子来说,感觉就是在说如何分类才能达到最好的问题.
这个问题研究的是Fish,如果被研究的对象有很多属性和方法通用的话,
可以定义成类,也直接把这些属性和方法写进来.
如果这些方法都要在子类中覆盖的话,那定义成接口比较好一些.
至于后面提到的分类方式:可爱的爬树的,可以吃的...
如果是单个子类拥有这些能力的话,可以把这些能力做为属性,
写在子类中,如果是大部分子类都具有这些属性的话,可以把每种
属性定义成接口,给子类实现.
一点愚见,还请高人指教.
qbquan 2007-10-11
  • 打赏
  • 举报
回复
颠覆楼主

(我就是纯粹jf的,你把我咋地?)
kulin3422 2007-10-08
  • 打赏
  • 举报
回复
up
nanfengbai 2007-10-04
  • 打赏
  • 举报
回复
走过,学习一下!
IhaveGotYou 2007-09-20
  • 打赏
  • 举报
回复
真正面临具体问题的时候,选择就会简单明了.
选择接口,就等于多个对象具备一类"action" 或则相同"property",体现"组装"
继承,比较"滥用"于搞"集成",搞"纠正子类错误"
很多的时候使用组合类是很好的选择.
这段时间在搞GWT,由于现在的版本不支持java.util.LinkedHashMap,又想使用.
摆在眼前的方案三个
1)建立新类,继承HashMap
2)建立新类,实现Map接口
3)使用组合类(HashMap+ArrayList)
最后好不犹豫选择了方案3),因为他最简单、实用(因为我只需要Map的几个功能)
bushuang 2007-09-12
  • 打赏
  • 举报
回复
上面谈的都很有道理,我发表下自己的愚见.
什么时候使用继承,到底是继承类还是实现接口关于这个话题一直就没有终止过.其实没必要分那么清楚.
接口的出现是因为单继承的语言的先天不足而出现的,它是对行为的抽象,只要分清楚了
单继承和多继承有什么区别,那么什么时候实现接口,什么时候继承类自然也就清楚了.
而单继承和多继承有什么区别呢,说白了就是一个只能有一个基类,一个可以有多个基类,分清楚了这个那问题就简单多了.在一个类可以继承自很多类时,其实继承哪一个都是可以的,但是我们需要考虑下,到底继承自哪个类是合理的,那怎么才算是合理呢,这个是要考虑很多方面的因素,比如重用性,再比如扩展性,就拿楼主说的那个网址里面的例子来说吧.比如要考虑重用性的话,如果我们的系统中要用到的主要是爬树的行为,并且以后还会有A鱼,B鱼,C鱼都要用到爬树的行为,以后重A鱼继承的D鱼,D鱼继承的E鱼也要用到爬树的行为,以后偶尔会出现F鱼,G鱼会用到能否吃的行为,那么完全可以让所有的鱼都从爬树这条继承链上继承下来,而能否吃完全可以做成一个接口.再比如如果要考虑扩展性的话,那么就需要仔细的分析一下了,到底以后会不会出现H鱼,I鱼等等需要用到能否吃的行为,J鱼,K鱼会用到会飞的行为呢.这个时候如果需要考虑扩展性那么就把爬树,能够吃,会飞全部都做成接口.
timerri 2007-09-05
  • 打赏
  • 举报
回复
1.如果继承能够让程序写起来更简单,就用继承
2.子类一定能替代父类?重载和多态怎么办???这理论还就只是理论!!oo的所有实现只是一个工具,别为了oo而oo,那是写八股文。
3.接口是写给别人看的,抽象类是写给自己用的。

实践者有自己的视角~~~
nirvana_li 2007-09-05
  • 打赏
  • 举报
回复
#TO:schumiXsuse(我记住了名字,却忘记了密码)
有道理。
#TO:lbx19822004()
挺有意思。
conquer_time 2007-08-28
  • 打赏
  • 举报
回复
书看多了。多多实践,就什么都明白了。
哈哈!
huanzhugege 2007-08-27
  • 打赏
  • 举报
回复
可以看看《J2EE开发全程实录》中对这个问题介绍的很详细。这有试读版,可以看看:http://book.csdn.net/bookfiles/427/index.html
愤怒的小蒋 2007-08-27
  • 打赏
  • 举报
回复
很难把握 我是初学者
lbx19822004 2007-08-27
  • 打赏
  • 举报
回复
呵呵,俺是个粗人,俺不觉得BLOG园的那位颠覆了传统,只是觉得他对于面向对象的思想还不够清晰,他帖子里的那个高手写的代码貌似菜鸟写的,所以不必惊讶,我认为:
面向对象里面的类是类型的意思,但我们使用的是对象(Object),也就是某些继承了父类,实现了一些接口的(最好有 final修饰的)子类,有是我到觉得父类们没有存在的必要,完全可以猴子就是猴子,猩猩就是猩猩不要给他们加个哺乳动物的父类,但是我们是编程,要讲什么代码简洁易读啦,什么可重用啦,所以才要去把类的公共属性和方法提出来搞个什么父类。
替换法则不是死的,需要就具体问题判断,世界上没有什么东西是绝对的(就向我不能说我现在所说的每一句话都正确一样)
抽象类一般用来充当父类,包含这个类别里合情理的公共属性和方法,子类则有更多的属性和方法的不同实现,抽象类的设计应在需求分析和设计之后确定,接口是为子类增加较奇特的方法或需于其它类联系时采用的,大个比方哺乳类的狗类的宠物狗类,哺乳类和狗类都是抽象类,宠物狗除了继承了哺乳类和狗类的属性和行为(方法)外,它还因为患上了狂犬病而咬人,这个咬人的方法我们开始没有把咬人的方法设计成狗类的抽象方法,所以我们给宠物狗实现一个狂犬病患者的接口,在接口里加咬人的抽象方法。如果哪天属于猫类的阿拉斯加流浪猫也不幸患上狂犬病,那她除了需要实现狂犬病患者的接口之外还需要实现流浪者接口了,因为并不是每只猫都应该具有流浪的行为。
恩。。话不能说太多了,不然会有板砖飞过来的。。。我闪~~
schumiXsuse 2007-08-27
  • 打赏
  • 举报
回复
1,什么时候应该使用继承
----------------------------------------------------------
一般说来,能用合成就不要用继承,继承最大的缺点就是破坏了封装性以及effictive java中提到的什么子类和父类的命名冲突。
什么时候用?他们关系确实是IS-A的时候,这个其实就不太好阐述清楚了,推荐继承的方式最好都采用GOF23中的模板方法模式,那样不会破化封装性。

2,Liskov替换法则 是不是就是 IS-A
-------------------------------------------------------------------
Liskov替换法是说用到父类的地方都可以用子类替换而软件功能单位不受到影响,看似说明一个继承的问题,但想想一个子类的实现都来源于父类具体实现的复用,替换掉子类的意义其实就不大了。所以子类更应该是父类的扩展而不是修改,代码实现的复用其实理应通过合成来实现,将父类实现的代码移到子类或辅助类,面向抽象编成(在代码中优先考虑复用抽象),这才是与开闭原则相符的。

3,接口和抽象类的区别(这个问题在很多面试题上已经见多了,希望听到更高层次的观点,诸如接口和抽象类如何实现的细节区别就不要说了)
---------------------------------------------------------------------------
如果继承抽象类是为了复用父类真正实现的代码,那么抽象类就跟接口完全不同。
如果继承抽象类是为了复用父类流程框架实现的代码(像模板方法模式那样),那么可以将抽象类看作一种特殊的接口,除了继承(实现)多和一的不同外。
UnknowREN 2007-08-24
  • 打赏
  • 举报
回复
当你的项目中有很多相同的东西的时候就该抽象出来一个做父类,留着被继承,如果你有个东西想到处用,而不影响现在的继承关系,比如jdk里面的serialize,这个时候就该用接口了。

一点愚见
createthread 2007-08-24
  • 打赏
  • 举报
回复
加入收藏,期待好解
nirvana_li 2007-08-24
  • 打赏
  • 举报
回复
原来CSDN也人气不旺了啊。
难道大家都去写博客了?呵呵
加载更多回复(10)

50,499

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧