作者leondemon (狗狗)
看板OOAD
标题Re: [问题] 学习物件导向初学
时间Sun Oct 25 01:03:24 2009
※ 引述《SpiritKnight (无心)》之铭言:
: 板上的前辈好我也想学习学习物件导向,我是电子系的,却对程式比
: 较有兴趣,但是因为本身不是资讯底子出身,所以对程式了解并没有
: 很深,都是上上课,学学指令,基本的什麽For,Do,If...等等的这些
: 学到都烂了,但是每当自己想提起手来写个程式的时候,小程式易写
: ,但是稍大一点的就像撞了墙一样,怎麽都感觉怪怪的,可是该会的
: 指令就明明都会,却不知道问题出在哪,後来一直有人建议我去了解
: 物件导向(虽然我不知道主因是否在此),但是一直常听到这个词,後
: 来不论是去上网查还是去书局翻书,发现不只"物件导向"这四个字我
: 认识,书里的每个字我也都认识,但是组合起来却似乎没有办法那麽
: 熟悉它到底在说什麽,通常不外乎就是一堆理论,想请教板上的高手
: ,有什麽书或是网站,是对物件导向深入浅出,并且有将物件导向与
: 传统的程式设计方式做相互比较的书,适合对物件导向很没概念的人
: 看的书吗? 谢谢各位前辈~
看到这篇实在是有感而发
小弟开始接触写程式也是半年内的事
更何况写的code没几行(最初接触原因是为了parse某个网站资讯)
最近毕业後 上个月才开始认真的研读程式语言和物件导向(因为真的有兴趣)
我发现「物件导向」四个字真的是个屁
很多人举「汽车」例子来解释物件 接着讲解实例(instance)和继承(inheritance)
然後封装(encapsulation)、抽象化(abstraction)和多型(polymorphism)则是含糊带过
初学者根本听不懂 这种说明真的是模糊了Object-Oriented的精神
到最後就是错误的使用OO 而不断的subclass和创造物件来撰写程式
这些完全没办法让学习者了解OO的真正使用方式 只是为了解释而解释
这让我学习OO的路上绕了一大段路
直到我念了欧莱礼出版的「深入浅出物件导向分析与设计」後才恍然大悟
(虽然这本书没有讲Object到底是什麽,接下来的内容完全是个人体悟,请多指教)
真的很希望能将Object-Oriented programming翻译成「以『目的』为取向的撰码」
我认为Object-Oriented:
是为了
管理和
操作而将
程序式撰码(procedural programming)分成许多
目的(object)撰码
而不是为了
物件(object)的
存在而去产生
属性(property)及
方法(method)
简单的说 OO是为了「管理」程式而将程式码和资料依照
目的(Object)来
分类(Class)管理
而不是为了要「模拟」某种
类别(Class)的
物件(Object)所应该具备的特质(属性及方法)
因此程式码之属性(变数)及方法(功能)是为了
同一目的而存在同一Object中 方便管理
而任何需要「管理」的 一定是个大东西 所以越大型程式用正确的物件导向越容易维护
OO最主要的精髓并不是在於继承 而是在於封装
封装(encapsulation)就是将程式码分开到特定的class 而这些程式码具有相同的目的
透过封装 将程式码依照不同的目的分门别类(class) 以方便使用与管理
透过封装 将不同的资料得以分开操作(instance) 以方便使用与管理
透过封装 可以将固定的程式码和经常变动的程式码分开 以方便管理和使用
继承(inheritance)是为了abstration而存在
**abstration大多翻译为「抽象化」但是我会重新翻译为「提取」
**由於我对於abstration这个字的意义有不同解读 因此本文关於这字的解释会有所不同
「提取」是将重复的程式码抽出来 封装一个class里面 简单的说是要避免程式码的重复
再藉由继承来分配到必须拥有这些特性的class当中
换句话说使用继承的目的是为了提取 将不同目的中 拥有共同的目的之特性抽取出来管理
而继承不是为了创造新的类别或物件
因此 没有需要abstration的理由 就没有需要inheritance的理由
多型(Polymorphism)是传递多种类别的接口 以达许多片段程式码的固定
**多型的解释需要code或图来帮助理解 但是这里只做文字解释(原谅我偷懒)
在继承当中 有个很重要的功能 就是覆写(overwrite) 也就是可以修改父类别的method
当你的程式码有某method都具有同源(homogenous,即本质相同)但有些许差异或不同行为
**用生物学眼光来解释 就是同源基因 但基因序列编码不同 =>希望能帮助生物人理解 Orz
覆写原本的目的是为了可以在继承大部分的父类别的程式码 又可保有修改些许功能的弹性
但是只为了覆写一些方法 却没有新增新的方法 而去subclass大型Object是个很蠢的作法
> 举例来说 同样是「飞(fly)」的方法 但是「动物类别」飞行的方法有很多种
> 但是无论你之後创造了什麽动物(鸟、蟑螂、飞鼠等翅膀或飞行模式并不同)
> 你都希望在你创造的「动物类别」中有一个「飞(fly)」的方法可以呼叫
> 初学者很容易就针对这个动物类别subclass出鸟、蟑螂等多个子类别
> 然後直接覆写「飞」的功能 (如果设计的动物不会飞 则将它覆写为没有功能)
> 这种就是为了「物件」的存在而去「继承」来写OO的方式
> 只为了「覆写」却没有新增任何功能而将类别进行subclass不是一个好的管理程式方式
> 较好的作法是将「飞行」视为一种「目的」而将其封装出来成为一个类别
> 该类别中只有一个「飞」的方法
> 然後针对「飞行」类别进行subclass(鸟类飞行、昆虫飞行、飞鼠飞行、蝙蝠飞行等)
> 覆写subclass中的「飞」的方法
> 最後在「动物」类别中 建立一个属性为「飞行」型态
> 然後只要在原先「动物」的类别中的「飞」去呼叫「飞行」类别中的「飞」
> 这样就可以不必在更改任何「动物」的code或是subclass
> 然後在创造实例(instance)後设定飞行的类别是何种「子类别」
**以上举例不懂没关系 因为我自己都快看不懂了 Orz
重点是 当你subclass的东西越庞大 就越容易出错或难以维护
而利用多型 就是将同源(同样名称)但不同功(功能)的方法 抽离并封装出来
利用inheritance/abstraction这样的关系 来重新覆写子类别的方法
这样只要在原先类别固定呼叫抽离封装出之类别的方法 实作则在该子类别当中
而只要在创造实例时决定呼叫哪种子类别(设为变数)即可
这样就可以固定原先类别的程式码 (以後要新增飞行方法都可以不必更改到动物类别)
也可避免对大型类别进行过度的subclass了
至於我认为什麽是Object?
其实前面说的很清楚了 Object就是某种「目的」
所有属於这个目的的方法、变数当应该分类(classify)到同一个类别(class)中
我将这些目的(Object)可以分为下面几种:
*资料分类目的:
此Object是为了存放不同实例(instance)的资料 而将这些相关变数放在一起
而其应该包含的方法应该都是与存取(accessor)或操作这些instance variables有关
这样可以在实作时将不同实例的的资料分开来管理和操作
其他无关的方法或属性则应该封装出来
*方法分类目的:
主要是操作相同目的之method 分类在同一类别中
而其所带有的变数(或称属性)则是为了这些方法所需纪录保存的参数
此Object的存在 最主要是提供不同的Object可以呼叫同样的功能
*架构固定目的:
简单的说 就是前两者Object中需要实行abstration出来的Object
存在的理由只有两种
1.将相同的方法和属性提取出来
2.为了使用多型
其目的都是为了避免重复程式码以减少需要的管理 将「固定」之物抽离出来固定
让变化的部份在继承中实作 (另:封装亦是将「变化」之物抽离出来管理)
因此这些「固定」出来的Object 即会变成一种较大的架构/骨架(因为几乎不会变动)
也就是因为主要利用abstraction/inheritance的「结果」来决定固定的架构
由於使用继承比使用封装来处理「变化」部份的程式码要来得简单
造成大部分的人在学习或使用OOP的时候 认为subclass才是OOD的精髓
却忘了OO一切都是用
封装来处理「
不同本质的变化之物」 这样有点本末倒置
而
继承只是用来处理「
相同本质的变化之物」也就是子类别和父类别本质上是相同的
(以前面提到的例子来说 「动物」和「飞行」在本质意义上是不同的
也就是在撰码时要修改的原因和存在目的并非一致 所以会封装出来再利用多型特性结合)
藉由委派(delegation)、聚集(aggregation)、合成(composition)等协同操作方式
是将不同的目的程式码(objects)整合成一个真正完整功能的物件(object)
所以良好的OOD
1.应该是要把不同目的之程式码分开 一种Object应该只有一种存在的目的
2.不会有任何重复的程式码 任何相同目的重复程式码都必须抽离出来
若是
同一物件中
多个方法会用到相同程式码 则抽离出来成为同物件下独立的
方法
若是
多个物件会用到相同程式码 则抽离出来成为独立一个的
物件下的方法
3.将应该十分固定及时常变更的程式码分开 即使目的相同
4.当一个理由需要新增或改写程式码时 需要维护或改变的Object应该是非常少的
而不是分散在四处都需要改变 减少程式发生错误的可能 才不会牵一发动全身
需要改变的地方越少 越容易维护和除错
5.适当的将程式码分配到不同Object可以快速了解程式的操作 能帮助了解整个程式架构
换句话说汽车(car)应该是由多个Objects协同组成
而不该把car设计成一个单独的Object(除非程式很小而且你又很懒得拆开来)
**如何拆解请参考欧莱礼出版的「深入浅出物件导向分析与设计」p393-395
--
以上是我对Object-Oriented的浅见 请多多指教 :)
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 163.13.42.121
※ 编辑: leondemon 来自: 163.13.42.121 (10/25 01:48)
1F:推 andrew43 :受用, 非常感谢. 10/25 01:43
※ 编辑: leondemon 来自: 163.13.42.121 (10/25 03:03)
2F:推 idleidle :先推後看 10/25 11:18
3F:→ adrianshum :最後一段只令我觉得你一知半解. car 是一个 class 10/25 17:57
4F:→ adrianshum :还是让由多个obj 组合, 诚如你所说, 该看你的 car 10/25 17:57
5F:→ adrianshum :目的是什麽. 这样没头没尾的说 "car 是一个obj 是错" 10/25 17:58
6F:→ adrianshum :实在不能苟同 10/25 17:58
7F:→ leondemon :我没说"car 是一个obj 是错"吧 =.= 10/26 11:44
8F:→ leondemon :我是说car应该要由多个Obj组成 除非没有理由要分开 10/26 11:44
9F:→ leondemon :举Car的例子 是因为它常会被视为"物件" 而对它撰码 10/26 11:50
10F:→ leondemon :如果用"物件"的思维 很容易就把所有程式码往它里面塞 10/26 11:51
11F:→ leondemon :但如果用"目的"去思维 就会重新思考程式码的封装方式 10/26 11:53
12F:→ leondemon :Object翻译成"目的"或"主题"会比"物件"提供思考帮助 10/26 11:55
13F:→ qrtt1 :如果不用他原先该有的想法去思考, 10/26 13:07
14F:→ qrtt1 :要同时扭曲很多解释的方式, 这样太过间接了 10/26 13:07
15F:→ TeaEEE :虽然打了很多字 不过不是很同意你很多的看法 10/26 17:56
16F:→ kanandg1 :要如何解释实作介面(或继承虚拟类别)呢? 10/27 16:48
17F:→ kanandg1 :他们没有程式码,不能算code 的reuse吧? 10/27 16:49
18F:→ leondemon :很欢迎大家指教与讨论 因为我也是还在学习! 10/27 19:15
19F:→ H45 :回文回文回文回文回文回文回文回文回文回文回文回文 10/27 20:19
20F:推 Davidjcan :我也是刚学习,我觉得原po写的不错 10/27 21:09
21F:推 adxis :有Aspect Oriented的味道 10/28 20:37
22F:推 adrianshum :"car...协同组成", "不该...单独的 Object" 10/29 11:39
23F:→ adrianshum :所以我才说, 视乎你设计 car 的目的是什麽, 没有应 10/29 11:40
24F:→ adrianshum :不应该协同组成. 另外, 我觉得把 object 想成目的才 10/29 11:41
25F:→ adrianshum :有问题. 建议你去看看 AOP, 那才真的是以目的出发. 10/29 11:41
26F:→ adrianshum :倒是我赞同, 定义 class 的时候, 该把每种 "目的" 10/29 11:48
27F:→ adrianshum :细分, 才能避免你提到那种 "通通都包" 的 class 10/29 11:49
28F:推 aecho :看到这麽长的文章还蛮感动的~~先推一下 11/04 22:19
29F:推 twntwn :不太同意, 物件导向是透过对真实世界的塑模来填补 12/16 05:52
30F:→ twntwn :从问题领域到解决方案领域间的gap.的一种方法论. 12/16 05:54
31F:→ twntwn :ood是物件导向流程中的一项活动,目的是导出解决方案, 12/16 05:56
32F:→ twntwn :有关抽像封装等设计物件的技术,OCP,LSP,DIP,SRP.ISP 12/16 05:58
33F:推 twntwn :可以去了解一下,因该会让你修正一下目前的想法。 12/16 06:01
34F:→ leondemon :谢谢大家的指教 我也只是OO初学者 我会努力上进的! 01/11 21:11
35F:推 larrywhy :好文推 仔细琢磨ing 04/09 13:55