最後一天旅程。今天前半段分享程式如何利用Polymorphism技術「重構」,後半段則是總結和心得報告。
if's
或case's
判斷物件,可考慮重構程式如果原本沒有使用繼承,又出現較長的if/elif
或match/case
來判斷物件,隨著系統擴展,陸續加入新物件或刪除原物件,於是elif或case就必須跟著訂正,而且訂正的可能不是一處而是幾百處。大家都知道,修改邏輯是有風險的,可能牽一髮而動全身。
此現象可能是由於原程式架構設計不良,導致日後擴充和維護不便,帶來較大風險。
先看原程式:
類別部分:
地球上的樹:
class EarthlyTree(): # 地球上的樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} is growing.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} is reproducing.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} is getting sick.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} is dying.')
金星樹:
class VenusianTree(): # 金星樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} grows younger and younger.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} reproduces on a daily basis.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} always revovers from illness.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} raises itself from the dead.')
火星樹:
class MartianTree(): # 火星樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} stops growing.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} does not reproduce.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} is healthy.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} is immortal.')
def jog(self): # 慢跑是火星樹的獨特行為。
print(f'{__class__.__name__} {self.breed} is jogging.')
主程式:
tree_infos = {'Earth': {'breed': 'ebony', 'age': 2_500},
'Venus': {'breed': 'vitex', 'age': -3_000},
'Mars': {'breed': 'mvule', 'age': 500_000_000}
}
tree_on_earth = EarthlyTree(tree_infos['Earth']['breed'], tree_infos['Earth']['age'])
tree_on_venus = VenusianTree(tree_infos['Venus']['breed'], tree_infos['Venus']['age'])
tree_on_mars = MartianTree(tree_infos['Mars']['breed'], tree_infos['Mars']['age'])
trees = {tree_infos['Earth']['breed']: tree_on_earth, tree_infos['Venus']['breed']: tree_on_venus, tree_infos['Mars']['breed']: tree_on_mars}
breed = input('Enter a tree breed: ').strip().lower() # 執行期間輸入樹種。
if trees.get(breed) is not None:
# 如果code有一大堆if/elif或match/case,就得考慮refactor為polymorphism了。
match breed:
case 'ebony': # 這些case's埋下日後維護上的地雷。
tree_on_earth.grow()
tree_on_earth.reproduce()
tree_on_earth.get_sick()
tree_on_earth.die()
case 'vitex':
tree_on_venus.grow()
tree_on_venus.reproduce()
tree_on_venus.get_sick()
tree_on_venus.die()
case 'mvule':
tree_on_mars.grow()
tree_on_mars.reproduce()
tree_on_mars.get_sick()
tree_on_mars.die()
else:
print('Oops, this tree is not in our list. Maybe you misspelled it?')
程式執行無誤,暫時相安無事。不過好景不長,不久後必須新增一個「太陽樹類別」。於是程式改寫如下:
類別:
class EarthlyTree(): # 地球上的樹
... # 實作略
class VenusianTree(): # 金星樹
... # 實作略
class MartianTree(): # 火星樹
... # 實作略
class SolarTree(): # 新增的太陽樹
... # 實作略
主程式:
tree_infos = {'Earth': {'breed': 'ebony', 'age': 2_500},
'Venus': {'breed': 'vitex', 'age': -3_000},
'Mars': {'breed': 'mvule', 'age': 500_000_000},
'Sun': {'breed': 'salix', 'age': 0}
}
tree_on_earth = EarthlyTree(tree_infos['Earth']['breed'], tree_infos['Earth']['age'])
tree_on_venus = VenusianTree(tree_infos['Venus']['breed'], tree_infos['Venus']['age'])
tree_on_mars = MartianTree(tree_infos['Mars']['breed'], tree_infos['Mars']['age'])
tree_on_sun = SolarTree(tree_infos['Sun']['breed'], tree_infos['Sun']['age'])
trees = {tree_infos['Earth']['breed']: tree_on_earth,
tree_infos['Venus']['breed']: tree_on_venus,
tree_infos['Mars']['breed']: tree_on_mars,
tree_infos['Sun']['breed']: tree_on_sun}
breed = input('Enter a tree breed: ').strip().lower() # 執行期間輸入樹種。
tree = trees.get(breed)
if tree is not None:
# 如果code有一大堆if/elif或match/case,就得考慮refactor為polymorphism了。
match breed:
case 'ebony': # 這些case's埋下日後維護的地雷。
tree_on_earth.grow()
tree_on_earth.reproduce()
tree_on_earth.get_sick()
tree_on_earth.die()
case 'vitex':
tree_on_venus.grow()
tree_on_venus.reproduce()
tree_on_venus.get_sick()
tree_on_venus.die()
case 'mvule':
tree_on_mars.grow()
tree_on_mars.reproduce()
tree_on_mars.get_sick()
tree_on_mars.die()
case 'salix': # 這裡要加一個case來判斷是否為太陽樹。
tree_on_sun.grow()
tree_on_sun.reproduce()
tree_on_sun.get_sick()
tree_on_sun.die()
else:
print('Oops, this tree is not in our list. Maybe you misspelled it?')
程式任何要判斷樹種的地方,都要加一個case 'salix':
,少改一個地方bugs就來了。更麻煩的是:有些bugs可不會立即出現,也許潛伏很長一段時間才爆發。筆者以前曾開發過一個系統,其中有一個bug隱藏了14年(沒寫錯,是14年)才發現。而且這個還不是潛水最久的bug呢。
這時該考慮將原系統重構(refactor)為多型機制了。方法就是昨天講的那招:增加一個抽樣類別作為介面,其他類別繼承自這個抽象類別並覆寫(override)其方法。說白就是將昨天的版本加一個太陽樹類別而已。由於主程式本來就沒有match/case
,商業邏輯改動幅度相對很小或壓根不必改。
重構後的完整程式如下:
類別:
from abc import ABC, abstractmethod
class Tree(ABC): # abstract class
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@abstractmethod
def grow(self): # 生長(樹的共同行為)
...
@abstractmethod # 繁殖(樹的共同行為)
def reproduce(self):
...
@abstractmethod # 生病(樹的共同行為)
def get_sick(self):
...
@abstractmethod # 死亡(樹的共同行為)
def die(self):
...
class EarthlyTree(Tree): # 地球上的樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} is growing.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} is reproducing.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} is getting sick.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} is dying.')
class VenusianTree(Tree): # 金星樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} grows younger and younger.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} reproduces on a daily basis.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} always revovers from illness.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} raises itself from the dead.')
class MartianTree(Tree): # 火星樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} stops growing.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} does not reproduce.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} is healthy.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} is immortal.')
def jog(self): # 慢跑是火星樹的獨特行為。
print(f'{__class__.__name__} {self.breed} is jogging.')
class SolarTree(Tree): # 太陽樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} grows for a billion years.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} reproduces nuclear power.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} never gets sick.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} will die in 7.5 billion years.')
def nuclear_fuse(self): # 核融合(聚變)是太陽樹的獨特行為。
print(f'{__class__.__name__} {self.breed} operates nuclear fusion.')
主程式:
tree_infos = {'Earth': {'breed': 'ebony', 'age': 2_500},
'Venus': {'breed': 'vitex', 'age': -3_000},
'Mars': {'breed': 'mvule', 'age': 500_000_000},
'Sun': {'breed': 'salix', 'age': 0}
}
tree_on_earth = EarthlyTree(tree_infos['Earth']['breed'], tree_infos['Earth']['age'])
tree_on_venus = VenusianTree(tree_infos['Venus']['breed'], tree_infos['Venus']['age'])
tree_on_mars = MartianTree(tree_infos['Mars']['breed'], tree_infos['Mars']['age'])
tree_on_sun = SolarTree(tree_infos['Sun']['breed'], tree_infos['Sun']['age'])
trees = {tree_infos['Earth']['breed']: tree_on_earth,
tree_infos['Venus']['breed']: tree_on_venus,
tree_infos['Mars']['breed']: tree_on_mars,
tree_infos['Sun']['breed']: tree_on_sun}
breed = input('Enter a tree breed: ').strip().lower()
tree = trees.get(breed)
if tree is not None:
tree.grow()
tree.reproduce()
tree.get_sick()
tree.die()
else:
print('Oops, this tree is not in our list. Maybe you misspelled it?')
試輸入剛才新增的太陽樹,結果成功呼叫到太陽樹的諸方法:
以上程式將資料和商業邏輯放在同一檔案,這是不好的。現在我們來切割一下,將資料部分抽出,重組成多個.py檔:
設定檔(tree_settings.py)
# tree_settings.py
import tree_classes
tree_infos = {'Earth': {'breed': 'ebony', 'age': 2_500},
'Venus': {'breed': 'vitex', 'age': -3_000},
'Mars': {'breed': 'mvule', 'age': 500_000_000},
'Sun': {'breed': 'salix', 'age': 0}
}
tree_on_earth = tree_classes.EarthlyTree(tree_infos['Earth']['breed'], tree_infos['Earth']['age'])
tree_on_venus = tree_classes.VenusianTree(tree_infos['Venus']['breed'], tree_infos['Venus']['age'])
tree_on_mars = tree_classes.MartianTree(tree_infos['Mars']['breed'], tree_infos['Mars']['age'])
tree_on_sun = tree_classes.SolarTree(tree_infos['Sun']['breed'], tree_infos['Sun']['age'])
trees = {tree_infos['Earth']['breed']: tree_on_earth,
tree_infos['Venus']['breed']: tree_on_venus,
tree_infos['Mars']['breed']: tree_on_mars,
tree_infos['Sun']['breed']: tree_on_sun}
類別檔(tree_classes.py)
為講解方便,目時將所有類別放在同一個檔案。其實拆分開一個類別一個檔案可能更好,方便個別管理維護。
# tree_classes.py # 以後再分拆成一個類別一個.py檔。
from abc import ABC, abstractmethod
class Tree(ABC): # abstract class
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@abstractmethod
def grow(self): # 生長(樹的共同行為)
...
@abstractmethod # 繁殖(樹的共同行為)
def reproduce(self):
...
@abstractmethod # 生病(樹的共同行為)
def get_sick(self):
...
@abstractmethod # 死亡(樹的共同行為)
def die(self):
...
class EarthlyTree(Tree): # 地球上的樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} is growing.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} is reproducing.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} is getting sick.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} is dying.')
class VenusianTree(Tree): # 金星樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} grows younger and younger.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} reproduces on a daily basis.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} always revovers from illness.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} raises itself from the dead.')
class MartianTree(Tree): # 火星樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} stops growing.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} does not reproduce.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} is healthy.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} is immortal.')
def jog(self): # 慢跑是火星樹的獨特行為。
print(f'{__class__.__name__} {self.breed} is jogging.')
class SolarTree(Tree): # 太陽樹
def __init__(self, breed: str, age: int): # constructor
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
@property
def breed(self) -> str:
return self.__breed
@breed.setter
def breed(self, breed: str):
is_valid_breed = True # 判斷條件略。
if is_valid_breed:
self.__breed = breed
else:
raise Exception('Invalid breed.')
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, age: int):
is_valid_age = True # 判斷條件略。
if is_valid_age:
self.__age = age
else:
raise Exception('Invalid age.')
def grow(self): # 生長。
print(f'{__class__.__name__} {self.breed} grows for a billion years.')
def reproduce(self): # 繁殖。
print(f'{__class__.__name__} {self.breed} reproduces nuclear power.')
def get_sick(self): # 得病。
print(f'{__class__.__name__} {self.breed} never gets sick.')
def die(self): # 死亡(假設死亡也有「行為」)。
print(f'{__class__.__name__} {self.breed} will die in 7.5 billion years.')
def nuclear_fuse(self): # 核融合(聚變)是太陽樹的獨特行為。
print(f'{__class__.__name__} {self.breed} operates nuclear fusion.')
主程式(poly_trees.py)
# poly_trees.py
from tree_settings import trees
breed = input('Enter a tree breed: ').strip().lower()
tree = trees.get(breed)
if tree is not None:
tree.grow()
tree.reproduce()
tree.get_sick()
tree.die()
else:
print('Oops, this tree is not in our list. Maybe you misspelled it?')
主程式變得非常簡潔,基本上新增刪除類別,主程式都不必更動。只要修改設定檔和類別檔就行。
如果進一步將設定檔改寫,存到諸如Excel或CSV等外部檔案,或存入資料庫,主程式再讀取這個外部檔案或從資料庫取得資料,程式將更加保險。終端使用者(end users)完全碰不到任何的Python code,資料有修改就改Excel或CSV檔。要是再寫一個user friendly的好用介面給終端使用者操作資料的修改新增刪除,那就更加理想。
在此對本系列文章給個總結及摘要。
物件.屬性
表示式存取。基本上類別使用者對他寫的物件.屬性
中的屬性究竟是attribute還是property,幾乎無感。@property
裝飾器實作property機制。@dataclass
裝飾器,是Python用來減輕碼農重複撰寫類別內boilerplate code的工具。self
,而類別方法的第一個參數則請用cls
。if/elif
或match/case
來判斷物件,很可能是設計不良的徵候。可考慮利用多型技術加以重構(refactor)。最後用以下對聯(註4)和大家互勉:
當從實地建基柱,莫在浮沙築高樓。
莎翁名句:
"Though this be madness, yet there is
method
in’t." (William Shakespeare, Hamlet, Act 2, Scene 2)
瘋而有「方
」。所以請放心,總找得到「方法
」的。
註1: 近來很多人在原三大支柱前加上Abstraction
作為另一支柱。本系列文則採三支柱論。
註2: 這裡的「後門」是指利用_<class>__<attribute>
方式,在類別外部以物件.屬性
表示式存取私有屬性。詳見本系列Day 8《在外部修改私有屬性,真行嗎?》。
註3: Java等語言以「介面」(Interface)機制來替代多重繼承。
註4: 本聯下聯出自曾寫過《多型與虛擬》一書的侯捷老師,上聯則為筆者狗「頭」續貂。