※ 引述《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