SlideShare a Scribd company logo
Object Oriented
Programming
kiwi
Outline
● 導言
○ 你心中的程式語言是?
○ 如何強化程式架構?
● 技術
○ OOP 物件導向的基本介紹
○ OOP 三大特色(封裝、繼承、多型)
○ OOP 三大特色的衍生
● 應用
○ 回頭看看 OOP 與程式語言的關聯
○ OOP 的簡單範例
● 補充
○ SOLID 心法
2
What is Code
程式語言是?
可能是
● 可以拿來開發有趣的玩意兒
● 可以拿來分析找出數據背後的含義
● 可以拿來讓人類發懶而自我毀滅
4
我們是如何用程式語言叫電腦做事情呢
sKiwitch Talk 1: 你能用程式做什麼
我的想法
5
想做的事情
轉化成電腦
能理解的程式語言
電腦理解命令後
做事
如何將想吩咐的命令,整理得簡單又好修改
所謂的工程師
6
if has egg:
buy 6 apples
else:
buy 1 apple
我的想法
7
想做的事情
轉化成電腦
能理解的程式語言
電腦理解命令後
做事
如何將想吩咐的命令,整理得簡單又好修改
同時更加精確呢?
一開始的我們
● 直接了當的命令
● 一口令一個動作,手把手慢慢帶
Problems
● 無法重覆使用
● 必須看完全部指令才知道要做什麼
8
# 兩數相加
a = input()
b = input()
print(a+b)
目標 → 往 節省(懶) / 好理解的方向
後來的我們
Solution
● 將程式碼變成 function (def)
● 變成黑盒子
Good
● 只需要知道 function 名字就可以
使用
def print_add_two_element():
first = input()
second = input()
print(first+second)
print_add_two_element()
9
Input Output
後來的我們
Solution
● 將程式碼變成 function (def)
● 變成黑盒子
Good
● 只需要知道 function 名字就可以
使用
def print_add_two_element():
first = input()
second = input()
print(first+second)
print_add_two_element()
10
BUT 只能達到這樣嗎??
Input Output
我們還有什麼可以優化?
11
如何強化程式架構?
12
可以優化的方向
● 希望 function 可以更加聰明,不需要記憶
function 名字
● 希望設定完資訊後,一鍵就能完成
● 如果 function 越來越多,希望能更有效的
管理
● ...
13
這是我的答案 - 機器人
● 幫我記住我喜歡的設定
● 叫他掃地,可以不用管他
● 可以直接使用
● ...
程式語言的 object oriented programing 就這樣出來了
(我是這樣猜得啦 (`Д´)
OOP 物件導向的基本介紹
14
15
超越 function 的功能 - Object Oriented Programming
目標
● [整合] 整合相關功能
● [簡易操作] 下單後初始化即可使用
● [記憶] 可記錄曾經輸入或處理的資訊
● [獨立] 每一台是獨立的
16
超越 function 的功能 - Object Oriented Programming
架構想法
1. 設計可重覆製造的
2. 加入記憶單元及動作
3. 彼此間互通
可重覆製造的 - Class/Instance
Class (類別)
● 藍圖(設計稿)
● 定義:
○ 資料
○ 互動行為
Instance (實例)
● 實體物件(可操作)
● 可直接
○ 對資料進行存取修改
○ 進行互動,改變資料
● Instance 彼此獨立不影響
建構子 (constructor)
依照 class 定義的藍圖,
建造實體可操作的物體
list
lst = list()
lst.append(‘a’)
lst_r = list()
lst_r.append(‘a’)
17
How to build class
class Car:
pass
>>> mycar = Car()
建構子 (constructor)
依照 class 定義的藍圖,
建造實體可操作的物體
18
但還不能幫我們做事
(,,Ծ‸Ծ,, )
19
超越 function 的功能 - Object Oriented Programming
架構想法
1. 設計可重覆製造的
a. class/instance
2. 加入記憶單元及動作
3. 彼此間互通
記憶單元 - Attribute
Attribute
Class 的記憶單元
● 可記錄已有的設定
● 可記錄先前的操作記錄
20
__init__ Instance 物件的建構函式
class Car:
def __init__(self):
self.speed = 5
self.direction = 'straight'
>>> mycar = Car()
>>> print(mycar.speed)
5
建構子 (constructor)
依照 class 定義的藍圖,
建造實體可操作的物體
21
More on __init__
class Car:
def __init__(self, name):
self.speed = 5
self.name = name
self.direction = 'straight'
>>> mycar = Car('kiwi')
>>> print(mycar.name)
kiwi
建構子 (constructor)
依照 class 定義的藍圖,
建造實體可操作的物體
22
總算有自己的名字了 (´∀`)
動作單元 - Method
Method
說明類別可以操作的行為
(或可視為 class 的 function)
ex. turn, move
● 使 class/instance 可以真正做事
23
Make it Move!!!!
class Car:
def __init__(self, name):
self.name = name
self.speed = 5
self.direction = 'straight'
def turn(self, direction):
self.direction = direction
def move(self):
print(f'{self.SPEED} pixels to {self.direction}')
>>> mycar = Car('kiwi')
>>> mycar.move()
5 pixels to Right
>>> mycar.turn('Right')
>>> mycar.move()
5 pixels to Right
建構子 (constructor)
依照 class 定義的藍圖,
建造實體可操作的物體
24
完成了最簡單的 Class 了
٩(๑❛ᴗ❛๑)۶
self - 代表目前的 class
● __init__
● attribute - self.attr
● method - self.func()
25
超越 function 的功能 - Object Oriented Programming
架構想法
1. 設計可重覆製造的
a. class/instance
2. 加入記憶單元及動作
a. attribute/method
3. 彼此間 attribute 的互通
a. class attribute/instance attribute
彼此間互通 - Attribute
Class Attribute
類別共通使用的變數
class Car:
MAX_SPEED = 5
Instance Attribute
實體化物件自己使用的
class Car:
def __init__(self, name):
self.name = name
class Dog:
kind = 'pomchi' # class attribute shared by all instances
def __init__(self, name):
self.name = name # instance attribute unique to each
instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # shared by all dogs
'pomchi'
>>> e.kind # shared by all dogs
'pomchi'
>>> d.name # unique to d
'Fido'
>>> e.name # unique to e
'Buddy'
26
小心使用可共享的 class attribute
Class attribute
● 可被相同 class 的修改
● 避免 Mutable object:
○ list / dict / set
class Dog:
tricks = [] # mistaken use of a class attribute
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
27
d e
tricks
list
Dog
28
超越 function 的功能 - Object Oriented Programming
架構
● [設計稿] Class
● [實體] Instance
● [記憶] Attribute
○ Class Attribute
○ Instance Attribute
● [動作] Method
29
30
31
超越 function 的功能 - Object Oriented Programming
目標
❖ 整合相關功能
❖ 下單後初始化即可使用
❖ 可記錄曾經輸入或處理的資訊
❖ 每一台是獨立的
架構想法
1. 設計可重覆製造的
a. class/instance
2. 加入動作及記憶單元
a. attribute/method
3. 彼此間 attribute 的互通
a. class attribute/instance attribute
32
超越 function 的功能 - Object Oriented Programming
架構
● [設計稿] Class
● [實體] Instance
● [記憶] Attribute
○ Class Attribute
○ Instance Attribute
● [動作] Method
有了架構例子,開始來實際使用看看
OOP 三大特色(封裝、繼承、多型)
33
嘗試設計一個狗狗
狗狗要會的
● 會叫
dog.call()
● 會吃
dog.walk_to_food()
dog.eat()
● 會小便
dog.walk_to_pee()
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def walk_to_food(self):
print('Find food')
def eat(self):
print('Open mouth')
def walk_to_pee(self):
print('Find bathroom')
def pee(self):
print('pee')
34
嘗試設計一個狗狗
狗狗要會的
● 會叫
dog.call()
● 會吃
dog.walk_to_food()
dog.eat()
● 會小便
dog.walk_to_pee()
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def walk_to_food(self):
print('Find food')
def eat(self):
print('Open mouth')
def walk_to_pee(self):
print('Find bathroom')
def pee(self):
print('pee')
35
過於複雜
小便要記得兩個動作
狗狗哭哭 ಥ⌣ಥ
36
超越 function 的功能 - Object Oriented Programming
原本希望能減輕負擔
但卻更累!
37
超越 function 的功能 - Object Oriented Programming
原本希望能減輕負擔
但卻更累!
Solution
希望能一個指令就能命令狗吃飯/上廁所
→ OOP 第一心法:封裝
Objec Oriented Programming 三大心法
封裝 Encapsulation
● 隱藏實作細節
● 透過類別接口操作
● 避免弄壞工具
38
封裝 Encapsulation
● 使用者
○ 無需了解細節,就可以使用
○ 類別有清晰的對外介面
● 編寫者
○ 提高功能相似度,減少對外的操控
○ 內部的實現可以自由地修改
39
BAD
GOOD
封裝 Encapsulation
Attribute
● 燃料
● 速度
Method
● 加速
● 保持速度
● 加油
Raise
● 沒有燃料
class Car:
def __init__(self):
self.fuel = 0
self.speed = 0
def acclerate(self):
self.speed += 1
self.run()
def run(self):
self.fuel -= self.speed
if self.fuel < 0:
raise StopError('No fuel!')
def refuel(self, fuel):
self.fuel += fuel
40
嘗試設計一個狗狗 v1
狗狗要會的
● 會叫
dog.call()
● 會吃
dog.walk_to_food()
dog.eat()
● 會小便
dog.walk_to_pee()
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def walk_to_food(self):
print('Find food')
def eat(self):
print('Open mouth')
def walk_to_pee(self):
print('Find bathroom')
def pee(self):
print('pee')
41
過於複雜
小便要記得兩個動作
狗狗哭哭 ಥ⌣ಥ
嘗試設計一個狗狗 v2
狗狗要會的
● 會叫
dog.call()
● 會吃
dog.eat()
● 會小便
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
42
Easy, happy
Objec Oriented Programming 三大心法
封裝 Encapsulation
● 隱藏實作細節
● 透過 class 接口操作
● 避免弄壞工具
43
嘗試設計一個狗狗 v2
狗狗要會的
● 會叫
dog.call()
● 會吃
dog.eat()
● 會小便
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
44
不想只有狗狗,也想要有貓
嘗試設計一個狗狗 v2
狗狗/貓貓要會的
● 會叫
dog.call()
● 會吃
dog.eat()
● 會小便
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
45
46
超越 function 的功能 - Object Oriented Programming
想直接借用狗的 class 來當貓的 class
47
超越 function 的功能 - Object Oriented Programming
想直接借用狗的 class 來當貓的 class
Solution
→ OOP 第二心法:繼承
Objec Oriented Programming 三大心法
封裝 Encapsulation
● 隱藏實作細節
● 透過 class 接口操作
● 避免弄壞工具
繼承 Inheritance
● 子繼承於父
● 子擁有父的屬性與方
法
48
繼承 Inheritance
目的:想創造一個相同功能不同類別
名字的類別
描述:子類別 是 父類別 的一種特殊
類型,是非常高的相關度。
功能
● child 擁有 parent 所有的
attribute 和 method
● 改變父類別,會連帶影響子類別
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
class Human:
def breathe(self):
print('breathe')
class Man(Human):
pass
class Woman(Human):
pass
49
>>> man = Man()
>>> man.breathe()
breathe
>>> wom = Woman()
>>> wom.breathe()
breathe
嘗試設計一個狗狗 v3
dog/cat 要會的
● 會叫
dog.call()
● 會吃
dog.eat()
● 會小便
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
class Cat(Dog):
pass
50
嘗試設計一個狗狗 v3
dog/cat 要會的
● 會叫
dog.call()
● 會吃
dog.eat()
● 會小便
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
class Cat(Dog):
pass
51
看似完美
但貓咪不是 bark 叫
〳 ° ▾ ° 〵
52
超越 function 的功能 - Object Oriented Programming
狗/貓能有各自的叫聲
53
超越 function 的功能 - Object Oriented Programming
狗/貓能有各自的叫聲
Solution
→ OOP 第三心法:多型
(相同的 method 有不同的 result)
Objec Oriented Programming
封裝 Encapsulation
● 隱藏實作細節
● 透過 class 接口操作
● 避免弄壞工具
繼承 Inheritance
● 子繼承於父
● 子擁有父的屬性與方
法
多型 Polymorphism
為
不同的 class
提供統一的method
但做不同的行為
54
多型 Polymorphism
● 基本上,子類別與父類別相同的一切
● 透過覆寫 override,取代父類別的行為
* override(覆寫) 與 overload(多載) 不一樣
class Dog:
def bark(self):
return NotImplemented
class Pug(Dog):
def bark(self):
print('pug pug')
class Beagle(Dog):
def bark(self):
print('beagle beagle')
class Dachshund(Dog):
def bark(self):
print('dachshund dachshund')
Bark
巴哥 小獵犬 臘腸犬
55
Override(覆寫) & Overload (多載)
Override
子類別將父類別的 method 重新定義
Overload (python 不支援)
相同名稱的 method,因 argument 不同而有不
同的結果。但可以用 *arg 或是 default
argument 模擬
56
class Dog:
def bark(self):
return NotImplemented
class Pug(Dog):
def bark(self):
print('pug pug')
class Beagle(Dog):
def bark(self):
print('beagle beagle')
class Dachshund(Dog):
def bark(self):
print('dachshund dachshund')
def say():
print('hello')
def say(txt):
print(txt)
def say(*args):
if len(args) == 1:
print(args[0])
else:
print('hello')
def say(txt=None):
if not txt:
print('hello')
else:
print(txt)
多型 Polymorphism - 強制使用 父類別
● 可透過 super() 呼叫父類別的
method (包含 __init__(self))
class Dog:
def bark(self):
print('bark')
class Pug(Dog):
def bark(self):
print('pug pug')
super().bark()
57
>>> pug = Pug()
>>> pug.bark()
pug pug
bark
class Human:
def __init__(self, gender):
self.gender = gender
print('Human create')
class Man(Human):
def __init__(self, name):
super().__init__('male')
self.name = name
More examples
58
月底
吃
嘗試設計一個狗狗 v3
dog/cat 要會的
● 會叫
dog.call()
● 會吃
dog.eat()
● 會小便
dog.pee()
class Dog:
def __init__(self, name):
self.name = name
def call(self):
print('barkkkkk')
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
class Cat(Dog):
pass
59
新建一個 Animal Class
作為父類別的準則
嘗試設計一個狗狗 v4
Animal 要會的
● 會叫
animal.call()
● 會吃
animal.eat()
● 會小便
animal.pee()
class Animal:
def __init__(self, name):
self.name = name
def call(self):
pass
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
class Dog(Animal):
pass
class Cat(Animal):
pass
60
嘗試設計一個狗狗 v5
Animal 要會的
● 會叫
animal.call()
● 會吃
animal.eat()
● 會小便
animal.pee()
class Animal:
def __init__(self, name):
self.name = name
def call(self):
pass
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
class Dog(Animal):
def call(self):
print('Barkkkk')
class Cat(Animal):
def call(self):
print('Meowwwwww')
61
完美!
Objec Oriented Programming
封裝 Encapsulation
● 隱藏實作細節
● 透過 class 接口操作
● 避免弄壞工具
繼承 Inheritance
● 子繼承於父
● 子擁有父的屬性與方
法
多型 Polymorphism
為
不同的 class
提供統一的method
但做不同的行為
62
OOP 三大特色的衍生
63
64
超越 function 的功能 - Object Oriented Programming
心法
● 封裝
● 繼承
● 多型
有了心法,我們再看看些特殊運用
衍生
● [繼承] 不同的繼承類型
● [繼承] 多重繼承
● [封裝] 組合
● [封裝][繼承] 妥善使用組合與繼承
● [繼承][多型] 抽象類別與介面的簡述
65
[繼承] 不同的繼承類型 Public
Public
● 外部/自己皆可呼叫
● 沒有限制
class Student:
# no underscore
def __init__(self, name):
self.name = name
66
[繼承] 不同的繼承類型 Protect
Protect
● 只允許自己呼叫
● 可繼承
class Student:
# single underscore
def __init__(self, name):
self._name = name
其實 Python 沒有限制
只是約定成俗的習慣
67
[繼承] 不同的繼承類型 Private
Private
● 只允許自己呼叫
● 不能繼承
class Student:
# dobule underscore
def __init__(self, name):
self.__name = name
Python 仍可存取
>>> kiwi = Student('kiwi')
>>> kiwi.__name
AttributeError: 'Student' object has
no attribute
>>> kiwi._Student__name
'kiwi'
68
[繼承] 不同的繼承類型
Private
● 只允許自己呼叫
● 不能繼承
Protect
● 只允許自己呼叫
● 可繼承
Public
● 外部/自己皆可呼叫
● 沒有限制
class Student:
# no underscore
def __init__(self, name):
self.name = name
class Student:
# single underscore
def __init__(self, name):
self._name = name
class Student:
# dobule underscore
def __init__(self, name):
self.__name = name
Python 沒有限制呼叫
但寫成這樣比較好閱讀 Python 仍可存取
>>> kiwi = Student('kiwi')
>>> kiwi.__name
AttributeError: 'Student' object has
no attribute
>>> kiwi._Student__name
'kiwi'
69
[繼承] 多重繼承
Python 支援多重繼承
● __init__ 會從 右至左 建立
● 從 左至右 做 method, attribute 搜尋
● 以上為基本上,詳細可搜尋他人寫的文章 Python 繼承 543 參考
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
70
__init__
Base3.__init__()
Base2.__init__()
Base1.__init__()
DerivedClassName.__init__()
特殊案例:鑽石繼承
[封裝] 組合
車子有多種元件組合在一起
● 引擎 (engine_power):提供動力給輪子
● 輪軸 (axle) :維繫輪子與方向
● 方向盤 (steering_wheel):修改輪軸方向
71
class Car:
def __init__(self):
self.engine_power = 0
self.axle = 'forward'
self.steering_wheel = 0
def turn_left(self):
self.steering_wheel -= 1
if self.steering_wheel < 0:
self.axle = 'left'
elif self.steering_wheel == 0:
self.axle = 'forward'
def turn_right(self):
self.steering_wheel += 1
if self.steering_wheel > 0:
self.axle = 'right'
elif self.steering_wheel == 0:
self.axle = 'forward'
def _accelerate(self):
self.engine_power += 1
def move(self):
if self.engine_power == 0:
print('NO Power. STOP!')
else:
print(f'Move {self.axle} with speed {self.engine_power}')
self.engine_power -= 1
[封裝][繼承] 妥善使用組合與繼承
72
不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a)
Fly
鳥類
No Fly
會飛的鳥類 不會飛的鳥類
[封裝][繼承] 妥善使用組合與繼承
73
不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a)
Fly
鳥類
No Fly
會飛的鳥類 不會飛的鳥類
But 企鵝?會游泳的?
[封裝][繼承] 妥善使用組合與繼承
74
不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a)
會飛的 會游泳的
會叫的
會叫的 會叫的
[封裝][繼承] 妥善使用組合與繼承
不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a)
Composition
Inheritance
75
Civil aircraft IS a plane.
Fighter aircraft IS a plane.
Airport HAS lots of planes, like Civil
aircraft and Fighter aircraft
[繼承][多型] 抽象類別的簡介
Abstract Class 抽象類別
不可實體化,定義規定
● 需要會吃/上廁所/叫
Concrete Class 具體類別
必須滿足抽象類別的規定
● 狗:bark
● 貓:喵
Instance 實體
● 我的狗狗
Animal
Cat
Dog
76
[繼承][多型] 抽象類別的簡介
feat. 嘗試設計一個狗狗 v5
Animal 要會的
● 會叫
animal.call()
● 會吃
animal.eat()
● 會小便
animal.pee()
class Animal:
def __init__(self, name):
self.name = name
def call(self):
pass
def eat(self):
print('Find food')
print('Open mouth')
def pee(self):
print('Find bathroom')
print('pee')
class Dog(Animal):
def call(self):
print('Barkkkk')
class Cat(Animal):
def call(self):
print('Meowwwwww')
77
[繼承][多型] 抽象類別的簡介
抽象類別(abstract class) 為
具體類別(concrete class) 定義基本概念
Good
● 讓未來要增加的需求功能,有明確的規範
● 使繼承類別有一致的介面
# Abstract Base Classes (ABC)
from abc import ABC
class Animal(ABC):
def call(self):
raise NotImplementedError('call not
implemented')
class Dog(Animal):
def call(self):
print('bark!')
class Cat(Animal):
pass
>>> dog = Dog()
>>> dog.call()
'bark!'
>>> cat = Cat()
>>> cat.call()
NotImplementedError: call not implemented
79
80
超越 function 的功能 - Object Oriented Programming
架構
● Class
● Instance
● Attribute
● Method
● Class Attribute/Instance Attribute
心法
● 封裝
● 繼承
● 多型
回到起點來檢視 What is Code
81
What is Code after u know OOP?
82
想做的事情
轉化成電腦
能理解的程式語言
電腦理解命令後
做事
透過 OOP 的三大特性(封裝/繼承/多型)
你可以如何運用
可以做的事變更多,但也越來越複雜
83
了解得越多,寫得越多,就越理解 寫程式就是不斷的在做選擇
84
寫扣這檔事
寫扣這檔事
做事啦!
1. 分析現階段目標
2. 確認未來可能的變化
3. 評估截止時間
4. 選擇適當的做法
85
ex.
隔天早上在高雄有研討會要開,
這時您的選擇是什麼?
How to write Clean Code!
寫程式如同寫份文章或報導
● 標題:可得知整篇想要討論的內容,使讀者決定是否該
讀下去
● 第一段:整篇的概要,不包細節
● 第一段之後:慢慢的揭露細節
不要讓讀者看完整段不知道含義
-> 強化文章段落重點
不要讓讀者一直翻來翻去
-> 減少文章段落參考
OOP 則是讓段落文章清晰的手段
86
強化文章段落重點 - Cohesion 內聚
類別中,彼此之間的關係。
● 低內聚
○ 在類別中,method 功能上差別很大
○ 在類別中,method 使用到的 data attribute 相關性很低
● 低內聚的後果
○ 無法從類別的名稱知道類別在做什麼
○ 維護上,因類別內的差異較大,相關模組連帶檢查
LOW HIGH
可單獨使用,單獨修改
87
減少文章段落參考 - Coupling 耦合
類別與類別之間資訊與參數依賴的程度
> 高耦合可能會導致漣漪效應,可複用性低
程度從高到低
● 內容耦合:直接使用他人模組資料
● 共享耦合:透過 global variable 互動
● 資料耦合:模組藉由傳入值共享資料,
每一個資料都是最基本的資料
88
高耦合
低耦合
超高耦合
How to write Clean Code!
寫程式如同寫份文章或報導
● 標題:可得知整篇想要討論的內容,使讀者決定是否該
讀下去
● 第一段:整篇的概要,不包細節
● 第一段之後:慢慢的揭露細節
不要讓讀者看完整段不知道含義
-> 強化文章段落內聚性(重點)
不要讓讀者一直翻來翻去
-> 減少文章段落耦合性(參考)
OOP 則是讓段落文章清晰的手段
89
What is the measurement for modules complexity
● 內聚 Cohesion:單一模組內部之間相互關係的評定標準
● 耦合 Coupling:用以評定兩個模組間的標準
增加段落重點,減少參考需求
提高內聚力,降低耦合度
90
OOP 的簡單範例
91
案例 1 計算機
功能
● 加減乘除
● M+/M-/MR/MC
○ M+: 記憶值 + 目前的數值
○ M-: 記憶值 - 目前的數值
○ MR: 顯示現在記憶值
○ MC: 歸零目前的記意值
92
Step.1 - 加減乘除
class Calculator:
def add(self, x, y):
self.x = x
self.y = y
a = self.x + self.y
return a
def subtract(self, x, y):
self.x = x
self.y = y
a = self.x - self.y
return a
def multiply(self, x, y):
self.x = x
self.y = y
a = self.x * self.y
return a
def divide(self, x, y):
self.x = x
self.y = y
if (y == 0):
a = "You can't divide by zero!"
else:
a = self.x / self.y 93
加入基本的加減乘除
案例 1 計算機
功能
● 加減乘除
● M+/M-/MR/MC
○ M+: 記憶值 + 目前的數值
○ M-: 記憶值 - 目前的數值
○ MR: 顯示現在記憶值
○ MC: 歸零目前的記意值
94
Step.2 - M+/M-/MR/MC
class Calculator:
def __init__(self):
self.mem = 0
def M_add(self, x):
self.mem += x
def M_subtract(self, x):
self.mem += x
def M_C(self):
self.mem = 0
def M_R(self):
return self.mem
95
加入 M+ / MR / MC
案例 2 - 車子
功能
● 有不同品牌的車子,對應不同上
限速度
● 每個車子有 前進/倒退/轉動
96
Step1 - 分析需求
1. 導入類別繼承的概念,先定義 Car 所需的
功能
2. Car 類別具備的需求
a. 前進
b. 倒退
c. 轉方向
1. 定義此 Car 類別所具備的 method
a. move()
b. accelerate()
c. brake()
d. turn_left()
e. turn_right()
f. show()
Class CAR 的 method
● move: 根據目前的速度做移動
○ 正數:往前
○ 負數:往後
● accelerate: 速度增加 1
● brake:速度減少 1
○ 減為負則視為倒車
● turn_left: 往左轉
● turn_right: 往右轉
● show: 目前速度
97
Step2 - 實作 Car 類別
class Car:
def __init__(self):
self.speed = 0
def move(self):
print(f'Move with {self.speed}')
def accelerate(self):
self.speed += 1
def brake(self):
self.speed -= 1
def turn_left(self):
print('Turn left')
def turn_right(self):
print('Turn right')
def show(self):
print(f'Speed: {self.speed}')
Class CAR 的 method
● move: 根據目前的速度做移動
○ 正數:往前
○ 負數:往後
● accelerate: 速度增加 1
● brake:速度減少 1
○ 減為負則視為倒車
● turn_left: 往左轉
● turn_right: 往右轉
● show: 目前速度
98
Step3 - 加入 concrete class (不同品牌的)
我們已經完成 Car 的抽象類別
1. 需求:各車品牌有自己的極限速度
2. 實作:
a. 限制 self.speed 的數值
b. 影響的 method 為 accelerate
99
Step4 - 實作
class Audi(Car):
MAX_SPEED = 5
def accelerate(self):
if self.speed < self.MAX_SPEED:
self.speed += 1
class Nissan(Car):
MAX_SPEED = 7
def accelerate(self):
if self.speed < self.MAX_SPEED:
self.speed += 1
class Volvo(Car):
MAX_SPEED = 9
def accelerate(self):
if self.speed < self.MAX_SPEED:
self.speed += 1
class Car:
def __init__(self):
self.speed = 0
def move(self):
print(f'Move with {self.speed}')
def accelerate(self):
if self.speed < self.MAX_SPEED:
self.speed += 1
...
100
1. 加入 class attribute MAX_SPEED 限制 2. 修改抽象類別:使 accelerate 加入限制
多餘的 accelerate
不同的 Car 類別呢?
Attribute
● 燃料
● 速度
Method
● 加速
● 保持速度
● 加油
Raise
● 沒有燃料
class Car:
def __init__(self):
self.fuel = 0
self.speed = 0
def acclerate(self):
self.speed += 1
self.run()
def run(self):
self.fuel -= self.speed
if self.fuel < 0:
raise StopError('No fuel!')
def refuel(self, fuel):
self.fuel += fuel
101
From 封裝 的 Car
不同的 Car 類別呢?
From 組合 的 Car
● 引擎 (engine_power):提供動力給輪子
● 輪軸 (axle) :維繫輪子與方向
● 方向盤 (steering_wheel):修改輪軸方向
102
class Car:
def __init__(self):
self.engine_power = 0
self.axle = 'forward'
self.steering_wheel = 0
def turn_left(self):
self.steering_wheel -= 1
if self.steering_wheel < 0:
self.axle = 'left'
elif self.steering_wheel == 0:
self.axle = 'forward'
def turn_right(self):
self.steering_wheel += 1
if self.steering_wheel > 0:
self.axle = 'right'
elif self.steering_wheel == 0:
self.axle = 'forward'
def _accelerate(self):
self.engine_power += 1
def move(self):
if self.engine_power == 0:
print('NO Power. STOP!')
else:
print(f'Move {self.axle} with speed {self.engine_power}')
self.engine_power -= 1
案例 3 - 寶可夢
功能
● 每個寶可夢有不同屬性
● 每一種寶可夢有自己的招式
● 同一種寶可夢仍有個性/素質上的
差異
● 依照等級解鎖招式
● ...
103
104
你會怎麼設計呢?
案例 4 - 嘗試設計一個狗狗
新功能
● v6 貓咪要會後空翻
● v7 狗狗不可以色色呢
● ...
105
不要去寫完美的架構
要去寫好修改易讀的架構!
進階:SOLID 心法
106
OOP SOLID 心法
Object Oriented Prgramming 只是提供概念跟工具
實作上要看欲解決的問題,再安排設計
● Single responsibility principle(SRP) 單一職責
● Open/close principle(OCP) 開放/封閉原則
● Liskov substitution principle(LSP) Liskov替換
● Interface Segregation Principle(ISP) 介面隔離
● Dependency Inversion Principle(DIP) 依賴反轉
107
Single responsibility principle(SRP) 單一職責
每個類別都應該只對唯一的一個角色負責,並且該角色應該能封裝起來
專注一個類別一個角色,可使功能
● 易於擴充
● 找到錯誤
方向 速度
108
Open/close principle(OCP) 開放/封閉原則
程式碼不斷的在推進
● 🆗 對於擴展是開放的(新增程式碼)
● ⛔ 對於更改是封閉的(更動程式碼)
> 易於擴展而不會修改而產生 crush
變化發生時,建立 抽象類別 來隔離以後發生的同類變化
抽象類別:輪胎表面材質
具體類別:[紋路] [鐵鍊]
平常
雪上
109
Liskov substitution principle(LSP) Liskov 替換
子類別應該跟隨父類別 (抽象類別) 的定義行為設計
● 🆗 各產牌的車子皆跟隨汽車這個概念
● ⛔ 矩形 -> 正方形
110
Parent Child
只能設定邊長
(違反
長寬設定彈性)
可設定長寬
Interface Segregation Principle(ISP) 介面隔離
不應被迫使用對其無用的方法或功能
● 只是想逛街,但卻帶了筆帶課本之類沒有用的
● 藉由下個技巧「依賴反轉」,扭轉依賴
111
Dependency Inversion Principle(DIP) 依賴反轉
依賴:A 的某一功能需要 B 類別才能正常運作,代表 A 依賴著 B
● 原始碼的繼承關係,應該只涉及抽象而不涉及具體。
● 具體實現則應該依賴於抽象介面
112
⛔人依賴著三明治(具體類別)
來補充能量
食物(抽象類別)
真正的食物(具體類別)
🆗 人依賴著食物來補充能量
這些食物可以是漢堡三明治魚
OOP SOLID 心法
Object Oriented Prgramming 只是提供概念跟工具
實作上要看欲解決的問題,再安排設計
● Single responsibility principle(SRP) 單一職責
● Open/close principle(OCP) 開放/封閉原則
● Liskov substitution principle(LSP) Liskov替換
● Interface Segregation Principle(ISP) 介面隔離
● Dependency Inversion Principle(DIP) 依賴反轉
113
結論
需求永遠在變動,程式永遠在變動
昨日的好架構,可能會是明日的痛苦
完全不懂
維護很累
濫用模式
困惑很多
漸漸釐清
模式刻意
無招勝有招
114
Thanks!
115

More Related Content

What's hot (6)

PPTX
Rancher
NamCx
 
PDF
[GUTS-RS] Testes de Performance
GUTS-RS
 
PPTX
Introduce AWS Lambda for newbie and Non-IT
Chitpong Wuttanan
 
PDF
Docker, mais qu’est-ce que c’est ?
Julien Maitrehenry
 
PDF
Docker Explained | What Is A Docker Container? | Docker Simplified | Docker T...
Edureka!
 
PDF
BDD with the Cucumber
Mücahit Kurt
 
Rancher
NamCx
 
[GUTS-RS] Testes de Performance
GUTS-RS
 
Introduce AWS Lambda for newbie and Non-IT
Chitpong Wuttanan
 
Docker, mais qu’est-ce que c’est ?
Julien Maitrehenry
 
Docker Explained | What Is A Docker Container? | Docker Simplified | Docker T...
Edureka!
 
BDD with the Cucumber
Mücahit Kurt
 

Similar to Object-oriented programming 物件導向程式設計 -- 從「需要」到「認識」,再到「應用」,最後內化在自己的寫扣裡 (20)

PDF
從 Classes 到 Objects: 那些 OOP 教我的事
Wen-Tien Chang
 
PDF
Python 自然語言處理應用 - 1. 環境配置篇 - 2024 / Environment Configuration
Yung-Ting Chen
 
PDF
Ch10
Alisha Smile
 
PDF
Ch10 教學
hungchiayang1
 
PDF
Python 2 - 快速簡介
Cheyin L
 
PPTX
流程語法與函式
Justin Lin
 
PDF
程式人雜誌 -- 2013 年 1 月 (創刊號)
鍾誠 陳鍾誠
 
PDF
2006 recycle opensourceprojects
George Ang
 
PDF
Recycle Open Source Projects
George Ang
 
PDF
4. 流程語法與函式
Justin Lin
 
PDF
Ch10 習題
hungchiayang1
 
PDF
Learn python 1
Chia-Hao Tsai
 
PDF
講義
Zhe An Li
 
PDF
Ruby Rails 老司機帶飛
Wen-Tien Chang
 
PDF
OO x Python @ Tainan.py x MOSUT x FP 2014.09.27
Chun-Yu Tseng
 
PDF
流程語法與函式
Justin Lin
 
ODP
Java 網路程式
PingLun Liao
 
PDF
物件導向系統分析與設計(建國)
Kyle Lin
 
PDF
Python 工作坊 (NCTU)
柏瑀 黃
 
PDF
Refactoring with Patterns in PHP
Jace Ju
 
從 Classes 到 Objects: 那些 OOP 教我的事
Wen-Tien Chang
 
Python 自然語言處理應用 - 1. 環境配置篇 - 2024 / Environment Configuration
Yung-Ting Chen
 
Ch10 教學
hungchiayang1
 
Python 2 - 快速簡介
Cheyin L
 
流程語法與函式
Justin Lin
 
程式人雜誌 -- 2013 年 1 月 (創刊號)
鍾誠 陳鍾誠
 
2006 recycle opensourceprojects
George Ang
 
Recycle Open Source Projects
George Ang
 
4. 流程語法與函式
Justin Lin
 
Ch10 習題
hungchiayang1
 
Learn python 1
Chia-Hao Tsai
 
講義
Zhe An Li
 
Ruby Rails 老司機帶飛
Wen-Tien Chang
 
OO x Python @ Tainan.py x MOSUT x FP 2014.09.27
Chun-Yu Tseng
 
流程語法與函式
Justin Lin
 
Java 網路程式
PingLun Liao
 
物件導向系統分析與設計(建國)
Kyle Lin
 
Python 工作坊 (NCTU)
柏瑀 黃
 
Refactoring with Patterns in PHP
Jace Ju
 
Ad

Object-oriented programming 物件導向程式設計 -- 從「需要」到「認識」,再到「應用」,最後內化在自己的寫扣裡

  • 2. Outline ● 導言 ○ 你心中的程式語言是? ○ 如何強化程式架構? ● 技術 ○ OOP 物件導向的基本介紹 ○ OOP 三大特色(封裝、繼承、多型) ○ OOP 三大特色的衍生 ● 應用 ○ 回頭看看 OOP 與程式語言的關聯 ○ OOP 的簡單範例 ● 補充 ○ SOLID 心法 2
  • 4. 可能是 ● 可以拿來開發有趣的玩意兒 ● 可以拿來分析找出數據背後的含義 ● 可以拿來讓人類發懶而自我毀滅 4 我們是如何用程式語言叫電腦做事情呢 sKiwitch Talk 1: 你能用程式做什麼
  • 6. 所謂的工程師 6 if has egg: buy 6 apples else: buy 1 apple
  • 8. 一開始的我們 ● 直接了當的命令 ● 一口令一個動作,手把手慢慢帶 Problems ● 無法重覆使用 ● 必須看完全部指令才知道要做什麼 8 # 兩數相加 a = input() b = input() print(a+b) 目標 → 往 節省(懶) / 好理解的方向
  • 9. 後來的我們 Solution ● 將程式碼變成 function (def) ● 變成黑盒子 Good ● 只需要知道 function 名字就可以 使用 def print_add_two_element(): first = input() second = input() print(first+second) print_add_two_element() 9 Input Output
  • 10. 後來的我們 Solution ● 將程式碼變成 function (def) ● 變成黑盒子 Good ● 只需要知道 function 名字就可以 使用 def print_add_two_element(): first = input() second = input() print(first+second) print_add_two_element() 10 BUT 只能達到這樣嗎?? Input Output
  • 12. 12 可以優化的方向 ● 希望 function 可以更加聰明,不需要記憶 function 名字 ● 希望設定完資訊後,一鍵就能完成 ● 如果 function 越來越多,希望能更有效的 管理 ● ...
  • 13. 13 這是我的答案 - 機器人 ● 幫我記住我喜歡的設定 ● 叫他掃地,可以不用管他 ● 可以直接使用 ● ... 程式語言的 object oriented programing 就這樣出來了 (我是這樣猜得啦 (`Д´)
  • 15. 15 超越 function 的功能 - Object Oriented Programming 目標 ● [整合] 整合相關功能 ● [簡易操作] 下單後初始化即可使用 ● [記憶] 可記錄曾經輸入或處理的資訊 ● [獨立] 每一台是獨立的
  • 16. 16 超越 function 的功能 - Object Oriented Programming 架構想法 1. 設計可重覆製造的 2. 加入記憶單元及動作 3. 彼此間互通
  • 17. 可重覆製造的 - Class/Instance Class (類別) ● 藍圖(設計稿) ● 定義: ○ 資料 ○ 互動行為 Instance (實例) ● 實體物件(可操作) ● 可直接 ○ 對資料進行存取修改 ○ 進行互動,改變資料 ● Instance 彼此獨立不影響 建構子 (constructor) 依照 class 定義的藍圖, 建造實體可操作的物體 list lst = list() lst.append(‘a’) lst_r = list() lst_r.append(‘a’) 17
  • 18. How to build class class Car: pass >>> mycar = Car() 建構子 (constructor) 依照 class 定義的藍圖, 建造實體可操作的物體 18 但還不能幫我們做事 (,,Ծ‸Ծ,, )
  • 19. 19 超越 function 的功能 - Object Oriented Programming 架構想法 1. 設計可重覆製造的 a. class/instance 2. 加入記憶單元及動作 3. 彼此間互通
  • 20. 記憶單元 - Attribute Attribute Class 的記憶單元 ● 可記錄已有的設定 ● 可記錄先前的操作記錄 20
  • 21. __init__ Instance 物件的建構函式 class Car: def __init__(self): self.speed = 5 self.direction = 'straight' >>> mycar = Car() >>> print(mycar.speed) 5 建構子 (constructor) 依照 class 定義的藍圖, 建造實體可操作的物體 21
  • 22. More on __init__ class Car: def __init__(self, name): self.speed = 5 self.name = name self.direction = 'straight' >>> mycar = Car('kiwi') >>> print(mycar.name) kiwi 建構子 (constructor) 依照 class 定義的藍圖, 建造實體可操作的物體 22 總算有自己的名字了 (´∀`)
  • 23. 動作單元 - Method Method 說明類別可以操作的行為 (或可視為 class 的 function) ex. turn, move ● 使 class/instance 可以真正做事 23
  • 24. Make it Move!!!! class Car: def __init__(self, name): self.name = name self.speed = 5 self.direction = 'straight' def turn(self, direction): self.direction = direction def move(self): print(f'{self.SPEED} pixels to {self.direction}') >>> mycar = Car('kiwi') >>> mycar.move() 5 pixels to Right >>> mycar.turn('Right') >>> mycar.move() 5 pixels to Right 建構子 (constructor) 依照 class 定義的藍圖, 建造實體可操作的物體 24 完成了最簡單的 Class 了 ٩(๑❛ᴗ❛๑)۶ self - 代表目前的 class ● __init__ ● attribute - self.attr ● method - self.func()
  • 25. 25 超越 function 的功能 - Object Oriented Programming 架構想法 1. 設計可重覆製造的 a. class/instance 2. 加入記憶單元及動作 a. attribute/method 3. 彼此間 attribute 的互通 a. class attribute/instance attribute
  • 26. 彼此間互通 - Attribute Class Attribute 類別共通使用的變數 class Car: MAX_SPEED = 5 Instance Attribute 實體化物件自己使用的 class Car: def __init__(self, name): self.name = name class Dog: kind = 'pomchi' # class attribute shared by all instances def __init__(self, name): self.name = name # instance attribute unique to each instance >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.kind # shared by all dogs 'pomchi' >>> e.kind # shared by all dogs 'pomchi' >>> d.name # unique to d 'Fido' >>> e.name # unique to e 'Buddy' 26
  • 27. 小心使用可共享的 class attribute Class attribute ● 可被相同 class 的修改 ● 避免 Mutable object: ○ list / dict / set class Dog: tricks = [] # mistaken use of a class attribute def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) >>> d = Dog('Fido') >>> e = Dog('Buddy') >>> d.add_trick('roll over') >>> e.add_trick('play dead') >>> d.tricks # unexpectedly shared by all dogs ['roll over', 'play dead'] 27 d e tricks list Dog
  • 28. 28 超越 function 的功能 - Object Oriented Programming 架構 ● [設計稿] Class ● [實體] Instance ● [記憶] Attribute ○ Class Attribute ○ Instance Attribute ● [動作] Method
  • 29. 29
  • 30. 30
  • 31. 31 超越 function 的功能 - Object Oriented Programming 目標 ❖ 整合相關功能 ❖ 下單後初始化即可使用 ❖ 可記錄曾經輸入或處理的資訊 ❖ 每一台是獨立的 架構想法 1. 設計可重覆製造的 a. class/instance 2. 加入動作及記憶單元 a. attribute/method 3. 彼此間 attribute 的互通 a. class attribute/instance attribute
  • 32. 32 超越 function 的功能 - Object Oriented Programming 架構 ● [設計稿] Class ● [實體] Instance ● [記憶] Attribute ○ Class Attribute ○ Instance Attribute ● [動作] Method 有了架構例子,開始來實際使用看看
  • 34. 嘗試設計一個狗狗 狗狗要會的 ● 會叫 dog.call() ● 會吃 dog.walk_to_food() dog.eat() ● 會小便 dog.walk_to_pee() dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def walk_to_food(self): print('Find food') def eat(self): print('Open mouth') def walk_to_pee(self): print('Find bathroom') def pee(self): print('pee') 34
  • 35. 嘗試設計一個狗狗 狗狗要會的 ● 會叫 dog.call() ● 會吃 dog.walk_to_food() dog.eat() ● 會小便 dog.walk_to_pee() dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def walk_to_food(self): print('Find food') def eat(self): print('Open mouth') def walk_to_pee(self): print('Find bathroom') def pee(self): print('pee') 35 過於複雜 小便要記得兩個動作 狗狗哭哭 ಥ⌣ಥ
  • 36. 36 超越 function 的功能 - Object Oriented Programming 原本希望能減輕負擔 但卻更累!
  • 37. 37 超越 function 的功能 - Object Oriented Programming 原本希望能減輕負擔 但卻更累! Solution 希望能一個指令就能命令狗吃飯/上廁所 → OOP 第一心法:封裝
  • 38. Objec Oriented Programming 三大心法 封裝 Encapsulation ● 隱藏實作細節 ● 透過類別接口操作 ● 避免弄壞工具 38
  • 39. 封裝 Encapsulation ● 使用者 ○ 無需了解細節,就可以使用 ○ 類別有清晰的對外介面 ● 編寫者 ○ 提高功能相似度,減少對外的操控 ○ 內部的實現可以自由地修改 39 BAD GOOD
  • 40. 封裝 Encapsulation Attribute ● 燃料 ● 速度 Method ● 加速 ● 保持速度 ● 加油 Raise ● 沒有燃料 class Car: def __init__(self): self.fuel = 0 self.speed = 0 def acclerate(self): self.speed += 1 self.run() def run(self): self.fuel -= self.speed if self.fuel < 0: raise StopError('No fuel!') def refuel(self, fuel): self.fuel += fuel 40
  • 41. 嘗試設計一個狗狗 v1 狗狗要會的 ● 會叫 dog.call() ● 會吃 dog.walk_to_food() dog.eat() ● 會小便 dog.walk_to_pee() dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def walk_to_food(self): print('Find food') def eat(self): print('Open mouth') def walk_to_pee(self): print('Find bathroom') def pee(self): print('pee') 41 過於複雜 小便要記得兩個動作 狗狗哭哭 ಥ⌣ಥ
  • 42. 嘗試設計一個狗狗 v2 狗狗要會的 ● 會叫 dog.call() ● 會吃 dog.eat() ● 會小便 dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') 42 Easy, happy
  • 43. Objec Oriented Programming 三大心法 封裝 Encapsulation ● 隱藏實作細節 ● 透過 class 接口操作 ● 避免弄壞工具 43
  • 44. 嘗試設計一個狗狗 v2 狗狗要會的 ● 會叫 dog.call() ● 會吃 dog.eat() ● 會小便 dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') 44 不想只有狗狗,也想要有貓
  • 45. 嘗試設計一個狗狗 v2 狗狗/貓貓要會的 ● 會叫 dog.call() ● 會吃 dog.eat() ● 會小便 dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') 45
  • 46. 46 超越 function 的功能 - Object Oriented Programming 想直接借用狗的 class 來當貓的 class
  • 47. 47 超越 function 的功能 - Object Oriented Programming 想直接借用狗的 class 來當貓的 class Solution → OOP 第二心法:繼承
  • 48. Objec Oriented Programming 三大心法 封裝 Encapsulation ● 隱藏實作細節 ● 透過 class 接口操作 ● 避免弄壞工具 繼承 Inheritance ● 子繼承於父 ● 子擁有父的屬性與方 法 48
  • 49. 繼承 Inheritance 目的:想創造一個相同功能不同類別 名字的類別 描述:子類別 是 父類別 的一種特殊 類型,是非常高的相關度。 功能 ● child 擁有 parent 所有的 attribute 和 method ● 改變父類別,會連帶影響子類別 class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> class Human: def breathe(self): print('breathe') class Man(Human): pass class Woman(Human): pass 49 >>> man = Man() >>> man.breathe() breathe >>> wom = Woman() >>> wom.breathe() breathe
  • 50. 嘗試設計一個狗狗 v3 dog/cat 要會的 ● 會叫 dog.call() ● 會吃 dog.eat() ● 會小便 dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') class Cat(Dog): pass 50
  • 51. 嘗試設計一個狗狗 v3 dog/cat 要會的 ● 會叫 dog.call() ● 會吃 dog.eat() ● 會小便 dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') class Cat(Dog): pass 51 看似完美 但貓咪不是 bark 叫 〳 ° ▾ ° 〵
  • 52. 52 超越 function 的功能 - Object Oriented Programming 狗/貓能有各自的叫聲
  • 53. 53 超越 function 的功能 - Object Oriented Programming 狗/貓能有各自的叫聲 Solution → OOP 第三心法:多型 (相同的 method 有不同的 result)
  • 54. Objec Oriented Programming 封裝 Encapsulation ● 隱藏實作細節 ● 透過 class 接口操作 ● 避免弄壞工具 繼承 Inheritance ● 子繼承於父 ● 子擁有父的屬性與方 法 多型 Polymorphism 為 不同的 class 提供統一的method 但做不同的行為 54
  • 55. 多型 Polymorphism ● 基本上,子類別與父類別相同的一切 ● 透過覆寫 override,取代父類別的行為 * override(覆寫) 與 overload(多載) 不一樣 class Dog: def bark(self): return NotImplemented class Pug(Dog): def bark(self): print('pug pug') class Beagle(Dog): def bark(self): print('beagle beagle') class Dachshund(Dog): def bark(self): print('dachshund dachshund') Bark 巴哥 小獵犬 臘腸犬 55
  • 56. Override(覆寫) & Overload (多載) Override 子類別將父類別的 method 重新定義 Overload (python 不支援) 相同名稱的 method,因 argument 不同而有不 同的結果。但可以用 *arg 或是 default argument 模擬 56 class Dog: def bark(self): return NotImplemented class Pug(Dog): def bark(self): print('pug pug') class Beagle(Dog): def bark(self): print('beagle beagle') class Dachshund(Dog): def bark(self): print('dachshund dachshund') def say(): print('hello') def say(txt): print(txt) def say(*args): if len(args) == 1: print(args[0]) else: print('hello') def say(txt=None): if not txt: print('hello') else: print(txt)
  • 57. 多型 Polymorphism - 強制使用 父類別 ● 可透過 super() 呼叫父類別的 method (包含 __init__(self)) class Dog: def bark(self): print('bark') class Pug(Dog): def bark(self): print('pug pug') super().bark() 57 >>> pug = Pug() >>> pug.bark() pug pug bark class Human: def __init__(self, gender): self.gender = gender print('Human create') class Man(Human): def __init__(self, name): super().__init__('male') self.name = name
  • 59. 嘗試設計一個狗狗 v3 dog/cat 要會的 ● 會叫 dog.call() ● 會吃 dog.eat() ● 會小便 dog.pee() class Dog: def __init__(self, name): self.name = name def call(self): print('barkkkkk') def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') class Cat(Dog): pass 59 新建一個 Animal Class 作為父類別的準則
  • 60. 嘗試設計一個狗狗 v4 Animal 要會的 ● 會叫 animal.call() ● 會吃 animal.eat() ● 會小便 animal.pee() class Animal: def __init__(self, name): self.name = name def call(self): pass def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') class Dog(Animal): pass class Cat(Animal): pass 60
  • 61. 嘗試設計一個狗狗 v5 Animal 要會的 ● 會叫 animal.call() ● 會吃 animal.eat() ● 會小便 animal.pee() class Animal: def __init__(self, name): self.name = name def call(self): pass def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') class Dog(Animal): def call(self): print('Barkkkk') class Cat(Animal): def call(self): print('Meowwwwww') 61 完美!
  • 62. Objec Oriented Programming 封裝 Encapsulation ● 隱藏實作細節 ● 透過 class 接口操作 ● 避免弄壞工具 繼承 Inheritance ● 子繼承於父 ● 子擁有父的屬性與方 法 多型 Polymorphism 為 不同的 class 提供統一的method 但做不同的行為 62
  • 64. 64 超越 function 的功能 - Object Oriented Programming 心法 ● 封裝 ● 繼承 ● 多型 有了心法,我們再看看些特殊運用
  • 65. 衍生 ● [繼承] 不同的繼承類型 ● [繼承] 多重繼承 ● [封裝] 組合 ● [封裝][繼承] 妥善使用組合與繼承 ● [繼承][多型] 抽象類別與介面的簡述 65
  • 66. [繼承] 不同的繼承類型 Public Public ● 外部/自己皆可呼叫 ● 沒有限制 class Student: # no underscore def __init__(self, name): self.name = name 66
  • 67. [繼承] 不同的繼承類型 Protect Protect ● 只允許自己呼叫 ● 可繼承 class Student: # single underscore def __init__(self, name): self._name = name 其實 Python 沒有限制 只是約定成俗的習慣 67
  • 68. [繼承] 不同的繼承類型 Private Private ● 只允許自己呼叫 ● 不能繼承 class Student: # dobule underscore def __init__(self, name): self.__name = name Python 仍可存取 >>> kiwi = Student('kiwi') >>> kiwi.__name AttributeError: 'Student' object has no attribute >>> kiwi._Student__name 'kiwi' 68
  • 69. [繼承] 不同的繼承類型 Private ● 只允許自己呼叫 ● 不能繼承 Protect ● 只允許自己呼叫 ● 可繼承 Public ● 外部/自己皆可呼叫 ● 沒有限制 class Student: # no underscore def __init__(self, name): self.name = name class Student: # single underscore def __init__(self, name): self._name = name class Student: # dobule underscore def __init__(self, name): self.__name = name Python 沒有限制呼叫 但寫成這樣比較好閱讀 Python 仍可存取 >>> kiwi = Student('kiwi') >>> kiwi.__name AttributeError: 'Student' object has no attribute >>> kiwi._Student__name 'kiwi' 69
  • 70. [繼承] 多重繼承 Python 支援多重繼承 ● __init__ 會從 右至左 建立 ● 從 左至右 做 method, attribute 搜尋 ● 以上為基本上,詳細可搜尋他人寫的文章 Python 繼承 543 參考 class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N> 70 __init__ Base3.__init__() Base2.__init__() Base1.__init__() DerivedClassName.__init__() 特殊案例:鑽石繼承
  • 71. [封裝] 組合 車子有多種元件組合在一起 ● 引擎 (engine_power):提供動力給輪子 ● 輪軸 (axle) :維繫輪子與方向 ● 方向盤 (steering_wheel):修改輪軸方向 71 class Car: def __init__(self): self.engine_power = 0 self.axle = 'forward' self.steering_wheel = 0 def turn_left(self): self.steering_wheel -= 1 if self.steering_wheel < 0: self.axle = 'left' elif self.steering_wheel == 0: self.axle = 'forward' def turn_right(self): self.steering_wheel += 1 if self.steering_wheel > 0: self.axle = 'right' elif self.steering_wheel == 0: self.axle = 'forward' def _accelerate(self): self.engine_power += 1 def move(self): if self.engine_power == 0: print('NO Power. STOP!') else: print(f'Move {self.axle} with speed {self.engine_power}') self.engine_power -= 1
  • 72. [封裝][繼承] 妥善使用組合與繼承 72 不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a) Fly 鳥類 No Fly 會飛的鳥類 不會飛的鳥類
  • 73. [封裝][繼承] 妥善使用組合與繼承 73 不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a) Fly 鳥類 No Fly 會飛的鳥類 不會飛的鳥類 But 企鵝?會游泳的?
  • 74. [封裝][繼承] 妥善使用組合與繼承 74 不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a) 會飛的 會游泳的 會叫的 會叫的 會叫的
  • 75. [封裝][繼承] 妥善使用組合與繼承 不要為了 code reuse 而使用繼承 (is a) ,可考慮使用組合 (has a) Composition Inheritance 75 Civil aircraft IS a plane. Fighter aircraft IS a plane. Airport HAS lots of planes, like Civil aircraft and Fighter aircraft
  • 76. [繼承][多型] 抽象類別的簡介 Abstract Class 抽象類別 不可實體化,定義規定 ● 需要會吃/上廁所/叫 Concrete Class 具體類別 必須滿足抽象類別的規定 ● 狗:bark ● 貓:喵 Instance 實體 ● 我的狗狗 Animal Cat Dog 76
  • 77. [繼承][多型] 抽象類別的簡介 feat. 嘗試設計一個狗狗 v5 Animal 要會的 ● 會叫 animal.call() ● 會吃 animal.eat() ● 會小便 animal.pee() class Animal: def __init__(self, name): self.name = name def call(self): pass def eat(self): print('Find food') print('Open mouth') def pee(self): print('Find bathroom') print('pee') class Dog(Animal): def call(self): print('Barkkkk') class Cat(Animal): def call(self): print('Meowwwwww') 77
  • 78. [繼承][多型] 抽象類別的簡介 抽象類別(abstract class) 為 具體類別(concrete class) 定義基本概念 Good ● 讓未來要增加的需求功能,有明確的規範 ● 使繼承類別有一致的介面 # Abstract Base Classes (ABC) from abc import ABC class Animal(ABC): def call(self): raise NotImplementedError('call not implemented') class Dog(Animal): def call(self): print('bark!') class Cat(Animal): pass >>> dog = Dog() >>> dog.call() 'bark!' >>> cat = Cat() >>> cat.call() NotImplementedError: call not implemented 79
  • 79. 80 超越 function 的功能 - Object Oriented Programming 架構 ● Class ● Instance ● Attribute ● Method ● Class Attribute/Instance Attribute 心法 ● 封裝 ● 繼承 ● 多型
  • 81. What is Code after u know OOP? 82 想做的事情 轉化成電腦 能理解的程式語言 電腦理解命令後 做事 透過 OOP 的三大特性(封裝/繼承/多型) 你可以如何運用
  • 84. 寫扣這檔事 做事啦! 1. 分析現階段目標 2. 確認未來可能的變化 3. 評估截止時間 4. 選擇適當的做法 85 ex. 隔天早上在高雄有研討會要開, 這時您的選擇是什麼?
  • 85. How to write Clean Code! 寫程式如同寫份文章或報導 ● 標題:可得知整篇想要討論的內容,使讀者決定是否該 讀下去 ● 第一段:整篇的概要,不包細節 ● 第一段之後:慢慢的揭露細節 不要讓讀者看完整段不知道含義 -> 強化文章段落重點 不要讓讀者一直翻來翻去 -> 減少文章段落參考 OOP 則是讓段落文章清晰的手段 86
  • 86. 強化文章段落重點 - Cohesion 內聚 類別中,彼此之間的關係。 ● 低內聚 ○ 在類別中,method 功能上差別很大 ○ 在類別中,method 使用到的 data attribute 相關性很低 ● 低內聚的後果 ○ 無法從類別的名稱知道類別在做什麼 ○ 維護上,因類別內的差異較大,相關模組連帶檢查 LOW HIGH 可單獨使用,單獨修改 87
  • 87. 減少文章段落參考 - Coupling 耦合 類別與類別之間資訊與參數依賴的程度 > 高耦合可能會導致漣漪效應,可複用性低 程度從高到低 ● 內容耦合:直接使用他人模組資料 ● 共享耦合:透過 global variable 互動 ● 資料耦合:模組藉由傳入值共享資料, 每一個資料都是最基本的資料 88 高耦合 低耦合 超高耦合
  • 88. How to write Clean Code! 寫程式如同寫份文章或報導 ● 標題:可得知整篇想要討論的內容,使讀者決定是否該 讀下去 ● 第一段:整篇的概要,不包細節 ● 第一段之後:慢慢的揭露細節 不要讓讀者看完整段不知道含義 -> 強化文章段落內聚性(重點) 不要讓讀者一直翻來翻去 -> 減少文章段落耦合性(參考) OOP 則是讓段落文章清晰的手段 89
  • 89. What is the measurement for modules complexity ● 內聚 Cohesion:單一模組內部之間相互關係的評定標準 ● 耦合 Coupling:用以評定兩個模組間的標準 增加段落重點,減少參考需求 提高內聚力,降低耦合度 90
  • 91. 案例 1 計算機 功能 ● 加減乘除 ● M+/M-/MR/MC ○ M+: 記憶值 + 目前的數值 ○ M-: 記憶值 - 目前的數值 ○ MR: 顯示現在記憶值 ○ MC: 歸零目前的記意值 92
  • 92. Step.1 - 加減乘除 class Calculator: def add(self, x, y): self.x = x self.y = y a = self.x + self.y return a def subtract(self, x, y): self.x = x self.y = y a = self.x - self.y return a def multiply(self, x, y): self.x = x self.y = y a = self.x * self.y return a def divide(self, x, y): self.x = x self.y = y if (y == 0): a = "You can't divide by zero!" else: a = self.x / self.y 93 加入基本的加減乘除
  • 93. 案例 1 計算機 功能 ● 加減乘除 ● M+/M-/MR/MC ○ M+: 記憶值 + 目前的數值 ○ M-: 記憶值 - 目前的數值 ○ MR: 顯示現在記憶值 ○ MC: 歸零目前的記意值 94
  • 94. Step.2 - M+/M-/MR/MC class Calculator: def __init__(self): self.mem = 0 def M_add(self, x): self.mem += x def M_subtract(self, x): self.mem += x def M_C(self): self.mem = 0 def M_R(self): return self.mem 95 加入 M+ / MR / MC
  • 95. 案例 2 - 車子 功能 ● 有不同品牌的車子,對應不同上 限速度 ● 每個車子有 前進/倒退/轉動 96
  • 96. Step1 - 分析需求 1. 導入類別繼承的概念,先定義 Car 所需的 功能 2. Car 類別具備的需求 a. 前進 b. 倒退 c. 轉方向 1. 定義此 Car 類別所具備的 method a. move() b. accelerate() c. brake() d. turn_left() e. turn_right() f. show() Class CAR 的 method ● move: 根據目前的速度做移動 ○ 正數:往前 ○ 負數:往後 ● accelerate: 速度增加 1 ● brake:速度減少 1 ○ 減為負則視為倒車 ● turn_left: 往左轉 ● turn_right: 往右轉 ● show: 目前速度 97
  • 97. Step2 - 實作 Car 類別 class Car: def __init__(self): self.speed = 0 def move(self): print(f'Move with {self.speed}') def accelerate(self): self.speed += 1 def brake(self): self.speed -= 1 def turn_left(self): print('Turn left') def turn_right(self): print('Turn right') def show(self): print(f'Speed: {self.speed}') Class CAR 的 method ● move: 根據目前的速度做移動 ○ 正數:往前 ○ 負數:往後 ● accelerate: 速度增加 1 ● brake:速度減少 1 ○ 減為負則視為倒車 ● turn_left: 往左轉 ● turn_right: 往右轉 ● show: 目前速度 98
  • 98. Step3 - 加入 concrete class (不同品牌的) 我們已經完成 Car 的抽象類別 1. 需求:各車品牌有自己的極限速度 2. 實作: a. 限制 self.speed 的數值 b. 影響的 method 為 accelerate 99
  • 99. Step4 - 實作 class Audi(Car): MAX_SPEED = 5 def accelerate(self): if self.speed < self.MAX_SPEED: self.speed += 1 class Nissan(Car): MAX_SPEED = 7 def accelerate(self): if self.speed < self.MAX_SPEED: self.speed += 1 class Volvo(Car): MAX_SPEED = 9 def accelerate(self): if self.speed < self.MAX_SPEED: self.speed += 1 class Car: def __init__(self): self.speed = 0 def move(self): print(f'Move with {self.speed}') def accelerate(self): if self.speed < self.MAX_SPEED: self.speed += 1 ... 100 1. 加入 class attribute MAX_SPEED 限制 2. 修改抽象類別:使 accelerate 加入限制 多餘的 accelerate
  • 100. 不同的 Car 類別呢? Attribute ● 燃料 ● 速度 Method ● 加速 ● 保持速度 ● 加油 Raise ● 沒有燃料 class Car: def __init__(self): self.fuel = 0 self.speed = 0 def acclerate(self): self.speed += 1 self.run() def run(self): self.fuel -= self.speed if self.fuel < 0: raise StopError('No fuel!') def refuel(self, fuel): self.fuel += fuel 101 From 封裝 的 Car
  • 101. 不同的 Car 類別呢? From 組合 的 Car ● 引擎 (engine_power):提供動力給輪子 ● 輪軸 (axle) :維繫輪子與方向 ● 方向盤 (steering_wheel):修改輪軸方向 102 class Car: def __init__(self): self.engine_power = 0 self.axle = 'forward' self.steering_wheel = 0 def turn_left(self): self.steering_wheel -= 1 if self.steering_wheel < 0: self.axle = 'left' elif self.steering_wheel == 0: self.axle = 'forward' def turn_right(self): self.steering_wheel += 1 if self.steering_wheel > 0: self.axle = 'right' elif self.steering_wheel == 0: self.axle = 'forward' def _accelerate(self): self.engine_power += 1 def move(self): if self.engine_power == 0: print('NO Power. STOP!') else: print(f'Move {self.axle} with speed {self.engine_power}') self.engine_power -= 1
  • 102. 案例 3 - 寶可夢 功能 ● 每個寶可夢有不同屬性 ● 每一種寶可夢有自己的招式 ● 同一種寶可夢仍有個性/素質上的 差異 ● 依照等級解鎖招式 ● ... 103
  • 104. 案例 4 - 嘗試設計一個狗狗 新功能 ● v6 貓咪要會後空翻 ● v7 狗狗不可以色色呢 ● ... 105 不要去寫完美的架構 要去寫好修改易讀的架構!
  • 106. OOP SOLID 心法 Object Oriented Prgramming 只是提供概念跟工具 實作上要看欲解決的問題,再安排設計 ● Single responsibility principle(SRP) 單一職責 ● Open/close principle(OCP) 開放/封閉原則 ● Liskov substitution principle(LSP) Liskov替換 ● Interface Segregation Principle(ISP) 介面隔離 ● Dependency Inversion Principle(DIP) 依賴反轉 107
  • 107. Single responsibility principle(SRP) 單一職責 每個類別都應該只對唯一的一個角色負責,並且該角色應該能封裝起來 專注一個類別一個角色,可使功能 ● 易於擴充 ● 找到錯誤 方向 速度 108
  • 108. Open/close principle(OCP) 開放/封閉原則 程式碼不斷的在推進 ● 🆗 對於擴展是開放的(新增程式碼) ● ⛔ 對於更改是封閉的(更動程式碼) > 易於擴展而不會修改而產生 crush 變化發生時,建立 抽象類別 來隔離以後發生的同類變化 抽象類別:輪胎表面材質 具體類別:[紋路] [鐵鍊] 平常 雪上 109
  • 109. Liskov substitution principle(LSP) Liskov 替換 子類別應該跟隨父類別 (抽象類別) 的定義行為設計 ● 🆗 各產牌的車子皆跟隨汽車這個概念 ● ⛔ 矩形 -> 正方形 110 Parent Child 只能設定邊長 (違反 長寬設定彈性) 可設定長寬
  • 110. Interface Segregation Principle(ISP) 介面隔離 不應被迫使用對其無用的方法或功能 ● 只是想逛街,但卻帶了筆帶課本之類沒有用的 ● 藉由下個技巧「依賴反轉」,扭轉依賴 111
  • 111. Dependency Inversion Principle(DIP) 依賴反轉 依賴:A 的某一功能需要 B 類別才能正常運作,代表 A 依賴著 B ● 原始碼的繼承關係,應該只涉及抽象而不涉及具體。 ● 具體實現則應該依賴於抽象介面 112 ⛔人依賴著三明治(具體類別) 來補充能量 食物(抽象類別) 真正的食物(具體類別) 🆗 人依賴著食物來補充能量 這些食物可以是漢堡三明治魚
  • 112. OOP SOLID 心法 Object Oriented Prgramming 只是提供概念跟工具 實作上要看欲解決的問題,再安排設計 ● Single responsibility principle(SRP) 單一職責 ● Open/close principle(OCP) 開放/封閉原則 ● Liskov substitution principle(LSP) Liskov替換 ● Interface Segregation Principle(ISP) 介面隔離 ● Dependency Inversion Principle(DIP) 依賴反轉 113

Editor's Notes

  • #4: 你覺得什麼是程式語言
  • #50: 例子修改 human / man / woman
  • #51: 加入實際 呼叫 Cat.call
  • #55: 修改多型的定義
  • #68: 加上 method 版本的
  • #73: 組合的意思?
  • #85: 做個 狗狗 v1 狗狗 v2 的比較
  • #91: 做軟體的人應該都知道,設計一個模組的基本精神,就是要『提高內聚力,降低耦合度』(如果原本不知道也沒關係,至少現在知道了)。提高內聚力的好處就是提高了模組的『獨立性』,也就是說這個模組可以被單獨使用,也可以被單獨修改。這兩點都很重要,因為可以被單獨使,就表示模組被『重複使用(reuse)』的機會變多了;可以被單獨修改,就表示開發人員可以放心大膽的修改模組而不用怕萬一改了模組之後會引發『漣波效應(ripple effect)』影響到其他原本功能正常的模組(就是可以避免不小心改一的地方錯十個地方)。 http://teddy-chen-tw.blogspot.com/2011/12/1.html 但是,軟體模組就跟人類一樣是『群居動物』,不太可能生活上食衣住行育樂一個人全部搞定。當然『程式是人寫出來的』,硬要寫出內聚力超高的模組也不是不可能。但是,一個內聚力超高的模組可能會發生以下兩個問題: 你的軟體只有一個模組,所有需要的程式碼與資料都在這個模組中。此模組內聚力超高,但是...一共有一萬行。這種程式,應該不太好維護吧。 為了不讓模組變得太大但是又要有很高的內聚力,當有新功能要開發的時候,你用 copy and past 的方式產生新的模組。新的模組內聚力很高,可以單獨執行(因為所需的資料與程式碼都被 copy 到新的模組中了),但是,這衍生了 duplicate code(重複程式碼)問題,此為軟體大忌,戒之,戒之。 所以,模組之間還是免不了需要『有關係』,也就是說耦合是難免的。但是不當的耦合(找小三,小四...XD),例如使用全域變數(global variables)就很容易產生一些很難看出來的 bugs(因為人人都可修改全域變數,萬一到時候資料不正確很難找到兇手)。如果是設計單一的 function 或是 method,那麼最低的耦合就是所謂的『資料耦合(data coupling)』,也就是說以參數傳遞的方式作為模組溝通的管道。但是,如果把『眼界放大一點』,看的是 design patterns 或是 architecture 中各個不同『角色(或是參與者)』的溝通方式要採用哪種耦合呢? 鄉民們不知道是否還記的『Design Patterns 分成三大類』這一篇,裡面有提到有一大類的 patterns 是屬於『patterns relying on abstract coupling』,包含了 State, Factory Method, Observer, Bridge, Builder, Command, Visitor, Interpreter, Mediator, Adapter, Prototype, Proxy,Strategy。那麼什麼又叫做『abstract coupling』?簡單的說,就是定義介面(interface),讓需要『有關係』模組透過介面來產生關係。所以 GoF 在 design patterns 書中開宗明義就說: program to an interface, not an implementation *** Abstract coupling 的概念應用很廣,像是 Layers 這個軟體架構上,下兩個 Layers 也是 abstract coupling 的關係。有一句話 Teddy 再強調一次: 模組之間要有關係(耦合)可以,但是最好只能有『精神外遇 抽象關係』,不要有『肉體耦合 實做耦合(implementation coupling)。』
  • #98: 導入封裝 避免複雜化
  • #104: 太冗 需要先給點 hint 不然就換案例
  • #112: 包包中攜帶了你不需要的東西,而你卻每次都要拿這個包包,導致你未曾想到過得麻煩