作者gnimnek168 (Gnimnek168)
看板Soft_Job
标题Re: [请益] 我这样解释OOP对吗?
时间Mon Nov 13 16:06:14 2017
1. 封装 (encapsulation)
其实封装本来就是人们面对复杂度的一种本能,针对某一问题点的广度与深度之间找到适
切的焦点。邓小平就曾说过:「不管黑猫或白猫,能抓到老鼠的就是好猫」。其实这就是把系
统当作黑箱 (black-box)的封装概念了。 :)
设计模式 (DP, Design Pattern)内的「Facade」,即为强调封装某一主体 (context)内
部繁杂的细节。
例如,两大平台的 Web MVC (Model/View/Control)是一种因应 Web 端的技术解决方案,
实际上 Controller 仅为 UI 端的控制逻辑,却不适合担任资料存取(data access)与逻
辑运算(business logic)的工作。所以在大型系统的开发上,一般会设计中间层的「领域
物件 (domain controller)」,将上述两大类的工作 (资料存取/逻辑运算)由其当窗口
(entry-poing),再视工作性质,委派 (delegate)给专司其职的成员物件 (如
DAO/Utility, Business Object ...等)。
此时「Domain Controller」就是一种系统的「Facade」物件,封装了资料存取与逻辑运
算的细节,UI 端 (Web/Standalone Form/Mobile App/外部系统)不需要知道如何处理,
只要能取得所需要的结果即可。
不仅程式写码,在 UML 的使用案例 (use case)需求分析技术中,就擅用了封装的技巧 (
系统功能(主题)->程序/工作事项->细节(资料栏位/计算逻辑)),先抓大的操作目的,再
来包容善变的细节。
在软体工程来说,这比较能造成「低耦合 (low coupling)」的效果。
--------------------------------------------------------------------------------------------------
2. 继承 (inheritance)=> 原意其实为「扩展 (extend)」较恰当
UML 是以「一般化-特殊化 (generalization-specialization」称之更为适切。
特殊化类别「扩展(extend)」一般化类别有两种用意:
a). 覆盖 (override)预设的行为 (UML 称 operation, OOP 称 method)。
b). 扩展原来所没有提供的行为。
举个简单的例子。「保险」有一预设的行为:计算保费(),但因为不同类型的保险,它们
的计算保费()逻辑实作均不一样。「意外险」、「疾病险」、「寿险」均为「保险」的特
殊化类别,因为有着不同的实作方式,所以由各自的特殊化类别 (OOP 较喜爱称为
sub-class)来分开个别实作。
这样有什麽好处?财务人员可以用「一视同仁」的角度 (保险),藉由共同的操作目的 (
计算保费() )来操作所有的特殊化类别 (意外/疾病/寿险),但执行时 (run-time)却是由
个别的特殊化类别负责实现。这样自然就形成後述所提「多型 (polymorphism)」的效果
了。
未来要再扩展不同保险的类型,只要再新增特殊化类别即可 (当然要负责该实作逻辑),
用户端 (Client)因为没有直接耦合,自然无需作过多的修改,也不致让程式码越形冗长
拥肿了。
P.S. 上述仅是领域概念 (domain concept)的解释,当转至软体的设计实作阶段,会再更
讲究精致些,诸如分析保险的各种行为,而抽离出一群群 (group)的特殊化类别出来 (如
状态群、业务逻辑群)。
--------------------------------------------------------------------------------------------------
3. 介面 (interface)/多型 (polymorphism)
介面 (对岸翻译为接口)是一种隔离的设计应用,让 Provider 不直接与众多甚而
Unknown Client 耦合,仅透过标准的介面制定规格 (spec.)沟通。
介面的应用在生活面比比皆是。「IPowerSupply」介面制定「供电(电流,伏特)」的规格
,Provider (台电)提供符合该规格的服务,让各类 Client (家电用品,电脑,游戏机)只
要能符合标准规格即能取得该服务 (供应电力);至於若如笔电并不符合市电规格
(110V,20A),所以需要经由调变器 (adapter)来作转型的工作。
软体设计实务上,一般就会建议 DAO (Data Access Object)最好为其定义存取的操作介
面,将规格与实作隔离。如此尔後若实作内容(或实作技术)变动,并不致影响到 Client
端的运作。
多型上述已有提及,就是一种「一视同仁」的态度,来操作一般化的行为,但实作仍由各
种特殊化类别来负责。而广义的多型设计,其实就不只限於所谓的「继承体系 (再强调一
次,继承这字眼不好,很容易误导。一般化/特殊化这比较适切)」,它同时也涵盖了介面
的应用,也就是一般化类型可以是具体(concrete)/抽象(abstract)类别,甚或完全没有
实作的介面(interface)。
观察设计模式所揭露诸多的物件模型,大致会分隔为上下两层。上层的一般化往往都会视
现在已知/未来变动的权衡,而定义为抽象类别或介面,再由後续的特殊化类别来扩展
(extend)或实现(realize),Client 端永远只与上层的一般化类型沟通,不直接与特殊化
类别耦合,而这就是一种因应变动性的设计考量。
总归上述 2,3 点,其目的就是让「各类型的物件责任更明确,所担负的行为更单一
(atomic)」。这在软体工程来说,即是「高内聚性 (high cohesion)」的展现。
--------------------------------------------------------------------------------------------------
以上这些观念确实为所谓物件导向分析/设计/实作 (OOA/OOD/OOP)基本功夫。主要目的是
「Design for Change」,却非是全然针对实作面「写出来」就好的议题。
这里就要能着实体会,OOP 所展现的程式码特质是强调「分散」,而非「集中」,也就是
让主要类型的物件有明确的责任 (responsibility, knowing/doing)。
试想想,原来可能只是把「计算订购总额()」的逻辑全给写在同一支程式码 (集中),但
物件导向的作法却很可能会把每一种的计算订购逻辑 (如考量 bonus/coupon/special
discount),从原来可能用 switch-case/if-then-else 的写法抽离至多个 sub-class上
,造成分散的效果。
一支程式码 (single-class)被拆解为多支程式码 (multiple-class),这对一般软体开发
人员 (尤其是入行没多久较在乎能否写出来的新手)而言,心态上是不容易接受的,更何
况会觉得这样更难 Debug?!
所以这类分散式特质的程式,比较会应用於大型善变化的系统,例如电子商务平台、ERP
、MIS,而产品设计的态度更是需要。所取得的回馈是让系统较有度的维护性、弹性与延
展性。其实个人更宁愿说:「创造系统高度的再利用价值」,这比较实在。
当然,分散式系统必须伴随两种配套:撰写单元测试程式 (unit-test code),以及持续
重构 (re-factoring)的态度。
分散式系统不会是一开始就能保障设计得很精确、一开始就是分散,那是一种持续重整的
过程与态度。但在重整/重构的过程中,如何确保已上线的功能不会被影响?当然在每一
次的修改前/後,都要跑过单元测试,那才有可能去调整修改程式码的。
** 可以至 FB 「软体设计鲜思维」社群内的档案区,下载关於「物件导向基础观念」
的简报,同时也有提供 C#.NET/Java 程式码的范例。另外也有其它关於软体设计相关的
案例 (C#.NET/Java 与 UML Model)可以下载参考。
https://www.facebook.com/groups/softthinking/
--
FB社团:软体设计鲜思维
https://www.facebook.com/groups/softthinking/
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 114.34.122.227
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Soft_Job/M.1510560382.A.6E9.html
1F:推 YahooTaiwan: 好像在考计概名词解释 XD 11/13 16:17
2F:推 kwpn: 第一篇不是有人推"三句没讲出重点就下一个问题了 谁会听你废 11/13 17:20
3F:→ kwpn: 话" 11/13 17:20
4F:→ elements: 考试大概可以拿一百 11/13 17:22
5F:推 kaufmann: 认真解答疑惑 11/13 17:35
6F:推 youtuuube000: 这样写根本看不懂... 11/13 17:45
7F:→ youtuuube000: 我只看到一堆专有名词而已 11/13 17:46
8F:推 ab18282099: 推详解 11/13 18:09
9F:推 buper: 先推解释 11/13 18:31
10F:推 WiseLin1125: 清楚明了 11/13 18:42
11F:→ bibo9901: 一本正经的废话 11/13 18:45
12F:嘘 pttuser: 这些背完,面试官都睡觉了 11/13 19:18
13F:嘘 pttuser: 问多型最简单的方法:C如何实现多型,会的话OO问完了 11/13 19:20
14F:嘘 pttuser: 不会的话,面试也问完了^o^ 11/13 19:23
15F:推 art1: 不看专有名词的部分应该很好懂吧,一般化/特殊化会看不懂? 11/13 19:30
16F:→ art1: 保险的例子也能看不懂? 11/13 19:30
17F:推 bibo9901: 这些概念跟本不需Object也能做到 11/13 19:50
18F:→ bibo9901: C没有object, FP跟本连class都没有, 难道就做不到封装 11/13 19:55
19F:→ bibo9901: 继承多态了? 这只是指出了「好程式」的特性, 并没有解释 11/13 19:57
20F:→ bibo9901: OO如何解决这些需求 11/13 19:57
21F:推 ripple0129: 搞不好面试官只是想要你回答Object Oriented Programm 11/13 20:10
22F:→ ripple0129: ing 11/13 20:10
23F:→ jej: 画一个class diagram 结束 11/13 20:16
24F:→ dreamnook: 好奇问问C的封装怎麽做xD 11/13 20:20
25F:推 willyann: 上次看到滴妹(阿滴英文)解释facade的意思,真是有那麽一 11/13 20:38
26F:→ willyann: 瞬认为她超适合当软体工程的代言人啊 11/13 20:38
27F:推 shiauji: 这篇会很难懂吗?我非本科觉得解释很精辟 11/13 21:09
28F:→ bibo9901: 不是难懂. 是太广泛到几乎是废话 11/13 22:42
29F:推 sss81521: 楼上你真的会OOP吗XD 他写这样你懂? 11/13 22:45
30F:→ sss81521: 我指sh大XD 11/13 22:46
31F:→ bibo9901: 就好像我问你「微波炉是什麽?」,而你的回答是 11/13 22:51
32F:推 bibo9901: 1.可以加热 2.可以放在家里 3.只要放得进去的食物都可以 11/13 22:53
33F:→ bibo9901: 当然是很好懂而且道理也没错....只是有讲跟没讲一样 11/13 22:54
34F:→ bibo9901: 比较有建设性的回答应该是加热原理、适用情形、使用规则 11/13 22:55
35F:推 vi000246: 我还是喜欢深入浅出拿鸭子跟pizza的范例 好记又好懂 11/13 23:28
36F:→ elements: 微波炉那个 要看你是面试厨师还是电机工程师 11/13 23:54
37F:推 art1: 没有解释如何解决吗? 那封装继承多型不算解决方法? 11/14 02:12
38F:推 shiauji: 这个 在做大型专案开发时不都要用到这些观念吗? 顺便讲 11/14 06:03
39F:→ shiauji: 个我原本是物理系的 不觉得这个多难懂.... 11/14 06:03
40F:→ keke0421: 外围画圈圈 却没有直击到核心 描述赘词太多 11/14 08:09
41F:推 silent5566: 懂OOP的看得懂这篇 不懂的还是不懂 11/14 09:15
42F:→ silent5566: 觉得可以用简单的譬喻或实做上的心得说明 11/14 09:16
43F:推 jefflu: 其实我觉得应该是 inheritance is for code reuse, and po 11/14 10:36
44F:→ jefflu: lymorphism is for extending the code. 11/14 10:36
45F:推 Vick753: 写的不错了 11/14 11:33
46F:推 aszx4510: 写得很棒 感谢分享 11/14 13:15
47F:推 ae86357961: 推 11/14 14:05
48F:→ LoserWon: OOP系列文第 2 名 11/14 17:09
49F:嘘 Sidney0503: 面试你会讲一堆中文杂英文? 11/16 12:38
50F:推 louie21099: 用心 推 05/15 15:08