面向對象是一種對現實世界理解和抽象的方法,是計算機編程技術發展到必定階段後的產物。面向對象編程(OOP)具體說是一種程序開發方法,一種編程思想、範式。你能夠粗濾的理解爲,項目經理乾的活就是面向對象,負責分配任務,而coder乾的活就是面向過程,負責具體任務的實施細節。
python
1.公式:程序 = 對象 + 交互程序員
2.優勢算法
(1) 思想層面:編程
-- 可模擬現實情景,更接近於人類思惟。python3.x
-- 有利於梳理概括、分析解決問題。數據結構
(2) 技術層面:app
-- 高複用:對重複的代碼進行封裝,提升開發效率。函數
-- 高擴展:增長新的功能,不修改之前的代碼。工具
-- 高維護:代碼可讀性好,邏輯清晰,結構規整。學習
3.缺點:學習曲線陡峭。
1.類(class):具備相同屬性(實例變量)和行爲(實例方法)的對象的抽象。實際上就是一種自定義的數據類型,類也有對應的類變量和類方法。
2.對象(instance):類的具體實例,即歸屬於某個類別的」個體」。
3.屬性和行爲:類與類方法不一樣,對象與對象屬性值不一樣。
1.格式
class 類名(object):
「」」文檔說明」」」
def _init_(self,參數列表):
self.實例變量名= 參數(形參)
def 實例方法名(self,參數列表):
pass
2.說明
-- 類名全部單詞首字母大寫,多個單詞不用下劃線隔開,採用駝峯體。
-- (object)是表示該類是從object類繼承下來的,python3.x版本可省略。
-- __init__也叫構造函數,建立對象時被調用,也能夠省略。
-- self綁定的是被建立的對象的內存地址,名稱能夠隨意。
拓展:object類和type類的區別,即基類與元類的區別
1.object類是繼承層面的:其餘的類或者對象都是經過繼承的關係,直接或者間接的繼承了object,翻閱全部對象的族譜,最後必定會發現它們的老祖宗就是object。
1 >>>list.__base__ #Python爲全部類都提供了一個__bases__屬性,經過該屬性能夠查看該類的全部直接父類,該屬性返回全部直接父類組成的元組 (<class 'object'>,) 2 >>>type.__bases__ (<class 'object'>,) 3 >>>object.__bases__ #object類是全部類的基類 ()
2.type類是類建立對象層面的:python中一切皆對象,數據、對象是由str、list等數據類型和自定義類建立的,而全部數據類型和自定義類都是由元類建立的
1 >>> type(list) <class 'type'> 2 >>> type(object) <class 'type'> 3 >>> type(type) <class 'type'> #type類是全部類的元類,可是其自己是由虛擬機建立的
1.格式
變量 = 類名 (實參數列表)
2.說明
-- python中無處不對象,對象是python中對數據的一種抽象的表示
-- 全部對象都有三種特性:id、類型、值
id:對象的內存地址,由變量關聯,查看經過:id(變量)
類型:生成對象的模型,對應類名,查看經過:type(變量)
值:對象中存放的數據,對應實參列表,查看經過:變量.__dict__
-- 每一個對象都是由其對應的類建立出來的
拓展:與對象的建立,調用,刪除相關的魔法方法:__new__()方法,__init__()方法,__call__()方法,__del__()方法
1.__new__()方法和__init__()方法:__new__()方法是一個類方法,在對象被建立的時候調用,該方法負責建立(實例化)一個對象。若是建立對象成功,該對象會自動的調用__init__()方法,對自身進行初始化,若是建立對象失敗,沒有對象了,天然也就沒誰去調用__init__()方法了。
2.__call__()方法:若是類中定義了該方法,可使對象成爲像函數和類同樣的可調用對象。
1 class Entity: 2 ''' 3 調用實體來改變實體的位置。 4 ''' 5 6 def __init__(self, size, x, y): 7 self.x, self.y = x, y 8 self.size = size 9 10 def __call__(self, x, y): 11 '''改變實體的位置''' 12 self.x, self.y = x, y 13 14 15 e = Entity(1, 2, 3) # 建立實例 16 print(e.__dict__) # {'x': 2, 'y': 3, 'size': 1} 17 e(4, 5) # 實例能夠象函數那樣執行,並傳入x y值,修改對象的x y 18 print(e.__dict__) # {'x': 4, 'y': 5, 'size': 1}
3.__del__()方法:是python垃圾回收機制的實際應用,當類中定義了該方法時,建立的對象的引用計數爲0時該方法被調用
>>> class D(object): def __init__(self): print 'this is D.__init__()' def __del__(self): print 'this is D.__del__()' >>> d = D() this is D.__init__() >>> d2 = d >>> d3 = d >>> >>> del d >>> del d2 >>> del d3 #當對象的引用計數爲0時自動觸發__del__()方法 this is D.__del__()
1.語法
(1) 定義:對象地址.變量名=數據值
(2) 調用:對象地址.變量名=數據值
2.說明
(1) 首次經過對象賦值爲建立,再次賦值爲修改。
(2) 一般在__init__構造函數中(self.實例變量名=形參)建立,建立對象時賦值。也能夠在
(3) 每一個對象存儲一份,經過對象地址訪問。
3.做用:描述對象自身的數據。
4.__dict__:對象的屬性,用於存儲自身實例變量(包括私有化變量)的字典。
1.語法
(1) 定義: def 方法名稱(self, 形參列表):
方法體
(2) 調用: 對象地址.實例方法名(實參列表)
2.說明
(1) 定義時至少有一個形參,通常命名爲"self",用於綁定調用這個方法的對象地址,經過對象調用實例方法時self不用傳參。
(2)每一個對象共用一份,經過對象地址訪問。不建議經過類名訪問實例方法(對象與對象數據值不一樣,經過類名調用實例方法時,須要給self參數傳參具體的對象)
3.做用:表示對象行爲。
1 """ 2 實例成員 3 記住一句話:實例成員,使用對象地址訪問. 4 """ 5 class Student: 6 pass 7 8 s01 = Student() 9 # 定義實例變量: 對象.變量名 = 數據值 10 s01.name = "lennie" 11 print(s01.name) #"lennie" 12 s02 = Student() 13 # print(s02.name)# AttributeError,由於s02指向的對象,沒有建立過實例變量name 14 # 經過__dict__獲取當前對象的全部實例變量 15 print(s01.__dict__) #{'name': 'lennie'} 16 print(s02.__dict__) #{} 17 18 19 class Student02: 20 def __init__(self, name, height): 21 self.name = name 22 self.height = height 23 24 # 實例方法 25 def print_self(self): 26 print(self.name,self.height) 27 28 29 s01 = Student02("lennie", 170) 30 s02 = Student02("ginger", 162) 31 # 建議:實例方法,經過對象地址訪問. 32 s01.print_self() 33 # 不建議:Student02.print_self(),若沒有傳遞對象地址,實例方法不能正確訪問對象數據. 34 Student02.print_self(s02)
1.語法
(1) 定義:在類中,方法外定義變量。
class 類名:
變量名 = 表達式
(2) 調用:類名.變量名
不建議經過對象訪問類變量
2.說明
(1) 存儲在類中。
(2)全部對象共享一份,可經過類或者對象直接調用。
3.做用:描述全部對象的共有數據。
1.語法
(1) 定義:
@classmethod
def 方法名稱(cls,形參列表):
方法體
(2) 調用:類名.方法名(實參列表)
不建議經過對象訪問類方法
2.說明
(1) 至少有一個形參,通常命名爲'cls',用於綁定類的內存地址。
(2) 使用@classmethod修飾的目的是調用類方法時能夠隱式傳遞類,即經過類名調用類方法時cls不須要傳參。
(3) 類方法中不能訪問實例成員,實例方法中能夠訪問類成員。
3.做用:操做類變量。
1 """ 2 類成員,用類的地址去訪問 3 """ 4 class ICBC: 5 # 類變量:總行的錢 6 total_money = 1000000 7 # 類方法 8 @classmethod 9 def print_total_money(cls): 10 # print(id(cls), id(ICBC)) 11 # cls : 存儲當前類的地址 12 # print("當前總行金額:",ICBC.total_money) 13 print("當前總行金額:", cls.total_money) 14 15 def __init__(self, name, money): 16 self.name = name 17 self.money = money 18 # 從總行扣除當前支行的錢 19 ICBC.total_money -= money 20 21 22 i01 = ICBC("天壇支行", 100000) 23 i02 = ICBC("陶然亭支行", 100000) 24 # 主流:經過類訪問類成員 25 ICBC.print_total_money() #"當前總行金額: 800000" 26 print(ICBC.total_money) #800000 27 # 非主流:經過對象訪問類成員 28 # print(i02.total_money) 29 # i02.print_total_money()
1.語法
(1) 定義:
@staticmethod
def 方法名稱(形參列表):
方法體
(2) 調用:類名.方法名(實參列表)
不建議經過對象訪問靜態方法
2.說明
(1) 使用@ staticmethod修飾的目的是該方法不須要隱式傳參數,即參數列表不含self,cls參數,其實就是個函數
(2) 靜態方法不能訪問實例成員和類成員
3.做用:定義經常使用的工具函數。
1.定義:將一些基本屬性封裝成共有屬性,類的建立中的__init__初始化
2.優點:將數據與對數據的操做相關聯,代碼可讀性(相比容器)更高(類是對象的模板)。
定義:類外提供必要的功能,隱藏實現的細節,私有化操做
優點:簡化編程,使用者沒必要了解具體的實現細節,只須要調用對外提供的功能。
私有成員:
(1) 做用:只有類內部能夠直接訪問,外部不能直接訪問,能夠對參數作檢查,避免傳入無效的參數
(2) 作法:__成員名
(3) 本質:障眼法,實際也能夠訪問。私有成員的名稱被修改成:_類名__成員名,能夠經過_dict_屬性或dir函數查看。
1 class Coder: 2 """ 3 私有成員的讀和寫操做 4 """ 5 def __init__(self, name, age): 6 self.name = name 7 self.__age = age #私有化方式一:私有成員命名採用雙下劃線開頭 8 # self.set_age(age) #私有化方式二 9 10 # 使用兩個公開的方法獲取和修改私有屬性 11 def get_age(self): 12 return self.__age # 類內部訪問無限制 13 14 def set_age(self, value): 15 if 0 <= value <= 100: # 檢驗數據的有效性,限制隨意更改屬性值 16 self.__age = value 17 else: 18 raise ValueError 19 20 age=property(get_age,set_age) #無讀寫限制 21 # age=property(get_age,None) #寫限制 22 # age=property(None,set_age) #讀限制 23 # age=property(None,None) #讀寫均限制 24 25 c = Coder("lennie", 26) 26 # 1.私有成員不能直接訪問 27 print(c.__age) #AttributeError: 'Coder' object has no attribute '__age' 28 print(c.__dict__) # {'name': 'lennie', '_Coder__age': 26} 29 # 2.可經過 對象._類名__成員名 訪問(此種訪問無限制) 30 c._Coder__age = 27 31 print(c._Coder__age) # 27 32 # 3.可經過兩個私有方法實現私有成員的讀寫(此種方式可限制私有成員被隨意改寫) 33 c.set_age(30) 34 print(c.get_age()) # 30 35 #4.可經過設置property(讀方法名,寫方法名),實現私有成員的讀寫 36 c.age=32 37 print(c.age) #32
4.屬性@property:
公開的實例變量,缺乏邏輯驗證。私有的實例變量與兩個公開的方法相結合,又使調用者的操做略顯複雜。而屬性能夠將兩個方法的使用方式像操做變量同樣方便。
(1) 定義:
@property #容許讀,等價於定義類變量:屬性名=property(讀方法名,None)
def 屬性名(self):
return self.__屬性名
@屬性名.setter #容許寫,等價於定義類變量:屬性名=property(讀方法名,None)
def 屬性名(self, value):
self.__屬性名= value
(2) 調用:
對象.屬性名 = 數據
變量 = 對象.屬性名
(3) 說明:
1 class Coder: 2 """ 3 @property和@私有成員名.setter 4 """ 5 def __init__(self, name, age): 6 self.name = name 7 self.__age = age #私有成員 8 9 @property 10 def age(self): 11 return self.__age 12 13 @age.setter 14 def age(self, value): 15 if 0 <= value <= 100: # 檢驗數據的有效性,限制隨意更改屬性值 16 self.__age = value 17 else: 18 raise ValueError 19 20 c=Coder("lennie",26) 21 # print(c.__age) #依然不能直接調用,AttributeError 22 #1.可直接由_類名__屬性名調用 23 c._Coder__age=27 24 print(c._Coder__age) #27 25 #2.避免使用兩個方法的繁瑣,使私有成員如變量般調用 26 c.age=30 27 # c.age=120 #ValueError 28 print(c.age) #30
(1) 分而治之
將一個大的需求分解爲許多類,每一個類處理一個獨立的功能。 設計角度看類的建立應先考慮方法(行爲,幹什麼),在考慮初始化(數據)
(2) 變則疏之 #拆分的度
變化的地方獨立封裝,避免影響其餘類。
(3) 高 內 聚
類中各個方法都在完成一項任務(單一職責的類)。
(4) 低 耦 合 #低不是杜絕,是有互相調用才能完成
類與類的關聯性與依賴度要低(每一個類獨立),讓一個類的改變,盡少影響其餘類。
2.優點:
便於分工,便於複用,可擴展性強。
class 父類:
def 父類方法(self):
方法體
class 子類(父類):
def 子類方法(self):
方法體
兒子 = 子類()
兒子.子類方法()
兒子.父類方法()
2.說明:子類直接擁有父類的方法.
1 """ 2 繼承 -- 方法 3 編程:代碼不用子類寫,可是能夠用. 4 """ 5 class Person: 6 def say(self): 7 print("說話") 8 9 class Student(Person): 10 def study(self): 11 print("學習") 12 13 class Teacher(Person): 14 def teach(self): 15 print("講課") 16 17 # 建立父類型對象,只能訪問父類型成員 18 p01 = Person() 19 p01.say() 20 21 # 建立子類型對象,能訪問父類型成員,還能訪自身成員 22 s01 = Student() 23 s01.say() 24 s01.study() 25 26 # "是否是 實例" 27 # s01的對象 是一種 Student類型 28 print(isinstance(s01,Student))# True 29 print(isinstance(s01,Person))# True isinstance用於鑑定對象的直接模板以及其父模板 30 print(isinstance(s01,Teacher))# False 31 32 # s01的類型 等於 Student類型 33 print(type(s01) == Student)# True 34 print(type(s01) == Person)# False type只能斷定對象的直接模板 35 36 # Student 類型 是一種 Person類型 37 print(issubclass(Student,Person))# True 38 print(issubclass(Student,Student))# True 39 print(issubclass(Student,Teacher))# False issubclass用於鑑定兩個類是否有親屬關係
class 子類(父類):
def __init__(self,參數列表):
super().__init__(參數列表)
self.自身實例變量 = 參數
2.說明
子類若是沒有構造函數,將自動執行父類的,但若是有構造函數將覆蓋父類的,此時必須經過super()函數調用父類的構造函數,以確保父類實例變量被正常建立。
重用現有類的功能,並在此基礎上進行擴展。子類直接具備父類的成員(共性),還能夠擴展新功能。
1 """ 2 繼承 -- 數據 3 """ 4 class Person: 5 def __init__(self, name=""): 6 self.name = name 7 8 class Student(Person): 9 def __init__(self, name="", score=0): 10 # self.name = name 11 # 若是子類沒有構造函數,使用父類構造函數 12 # 若是子類有構造函數,必須銅經過super()調用父類構造函數, 13 # 不然會覆蓋父類(不執行)的. 14 super().__init__(name) 15 self.score = score 16 17 18 # 建立實例變量 19 s01 = Student("lennie", 100) 20 print(s01.name) #"lennie" 21 print(s01.score) #100 22 p01 = Person("ginger") 23 print(p01.name) #"ginger" 24 print(p01.score) #報錯AttributeError,子直接繼承父,但父不能調用子類成員
一種代碼複用的方式。
耦合度高:父類的變化,直接影響子類。
將相關類的共性進行抽象,統一律念,隔離變化。類是對象的抽象,同理父類是子類的抽象。
多個類在概念上是一致的,且須要進行統一的處理。
1 """ 2 繼承 -- 設計角度。隔離變化 3 老張開車去東北. 4 需求變化:還可能坐飛機,坐火車...... 5 """ 6 7 8 class Person: 9 def __init__(self, name=""): 10 self.name = name 11 12 def go_to(self,str_position,vehicle): 13 #經過一個參數vehicle將對象傳遞進來,從而調用方法transport(),
鴨子類型的概念(此處傳入的對象只要有transport()方法,就視爲也是Vehicle類型,並不是必定是其子類) 14 print(self.name,vehicle.transport(),"去",str_position,sep="") 15 16 17 class Vehicle: 18 """ 19 交通工具類,抽象的 20 將共有的功能(運輸)進行提取,供其子類對象繼承複用 21 """ 22 def transport(self): 23 pass 24 25 # -------------------------------------隔離變化 26 class Car(Vehicle): 27 28 def transport(self): 29 return "坐車" 30 31 class Airplane(Vehicle): 32 33 def transport(self): 34 return "坐飛機" 35 36 37 c01 = Car() 38 a01 = Airplane() 39 lz = Person("老張") 40 lz.go_to("東北",c01) #老張坐車去東北 41 lz.go_to("東北",a01) #老張坐飛機去東北
一個子類繼承兩個或兩個以上的基類,父類中的屬性和方法同時被子類繼承下來。
MRO(Method Resolution Order)查看同名方法的解析順序:print(D.mro)類自身 --> 父類繼承列表(由左至右)--> 再上層父類
A
/ \
B C
\ /
D
1 class A: 2 pass 3 class B(A): 4 pass 5 class C(A): 6 pass 7 class D(B,C): 8 pass 9 10 print(D.mro())#[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] 11 print(C.mro())#[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>] 12 print(B.mro())#[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>] 13 print(A.mro())#[<class '__main__.A'>, <class 'object'>]
父類的同一種動做或者行爲,在不一樣的子類上有不一樣的實現。
子類中定義(def)了父類中同名的方法(方法名、參數)。雖然該同名方法在子類中定義了,依然認爲是父類的方法。
在調用該方法時,實際執行的是子類的方法。
Ctrl + O
Python中,以雙下劃線開頭、雙下劃線結尾的是系統定義的成員。咱們能夠在自定義類中進行重寫,從而改變其行爲。
__str__函數:將對象(打印時顯示內存地址)轉換爲字符串(打印對象時顯示自定義字符串)(對人友好的)
__repr__函數:將對象轉換爲字符串(解釋器可識別的)
1 """ 2 內置可重寫函數 3 自定義對象 --> str 4 """ 5 class Car: 6 def __init__(self, brand="", price=0,max_speed = 0): 7 self.brand = brand 8 self.price = price 9 self.max_speed = max_speed 10 11 # # 對人友好的,爲所欲爲的規定字符串內容-->將<__main__.Car object at 0x...>轉換爲自定義的字符串 12 # def __str__(self): 13 # return "品牌是%s,單價是%d"%(self.brand,self.price) 14 15 # 對解釋器友好,根據python語法規定字符串內容-->功能同__str__(),可是更強大,一般與eval("")配合使用 16 def __repr__(self): 17 return "Car('%s',%d,%d)"%(self.brand,self.price,self.max_speed) 18 19 # 應用:將對象換成自定義的字符串顯示出來 20 c01 = Car("寶馬",1000000,260) 21 print(c01) #"品牌是寶馬,單價是1000000",會自動調用c01.__str__()方法 22 #當已經定義了__str__()時必須顯式調用c01.__repr__()或者repr(c01)纔會執行 23 print(c01.__repr__()) #"Car('寶馬',1000000,260)"
拓展:eval("代碼")函數
>>> eval("1+2") 3 >>> eval("lennie") #等價於在命令行直接輸入lennie NameError: name 'lennie' is not defined >>> lennie NameError: name 'lennie' is not defined >>>
4.應用:eval("")函數與__repr__()方法配合使用,實現深拷貝對象
1 # 定義技能類(技能名稱,攻擊比例,持續時間) 2 # 建立技能對象,直接print對象. 3 # 克隆(深拷貝)技能對象,體會改變其中一個,不影響另一個. 4 5 class Skill: 6 def __init__(self, name="", atk_ratio=0.1, duration=0.1): 7 self.name = name 8 self.atk_ratio = atk_ratio 9 self.duration = duration 10 11 def __str__(self): 12 return "%s---%d---%d" % (self.name, self.atk_ratio, self.duration) 13 14 def __repr__(self): 15 return "Skill('%s',%d,%d)" % (self.name, self.atk_ratio, self.duration) 16 17 18 s01 = Skill("佛怒火蓮", 3, 5) 19 print(s01) #佛怒火蓮---3---5 20 21 # s02 = eval("Skill('佛怒火蓮',3,5)") 22 s02 = eval(s01.__repr__()) 23 s01.name = "焰分噬浪尺" 24 print(s02) #佛怒火蓮---3---5
定義:讓自定義的類生成的對象(實例),可以像str、int、list類型數據對象同樣,使用運算符進行操做。
1 """ 2 運算符重載(重寫) 3 """ 4 5 # 多態:調用父,執行子. 6 class Vector: 7 """ 8 向量 9 """ 10 11 def __init__(self, x=0): 12 self.x = x 13 14 def __str__(self): 15 return "份量是:" + str(self.x) 16 17 # 算數運算符 --> 返回新結果 18 def __add__(self, other): 19 """ 20 當前對象與其餘數據相加時被自動調用 21 :param other: 其餘數據 22 :return: 向量 23 """ 24 # 若是other 是 向量 25 if type(other) == Vector: 26 return Vector(self.x + other.x) 27 # 不然 28 else: 29 return Vector(self.x + other) 30 31 def __rsub__(self, other): 32 return Vector(other - self.x) 33 34 # 複合運算符 --> 在原有對象基礎上修改 35 def __iadd__(self, other): 36 self.x += other 37 return self 38 39 def __gt__(self, other): 40 return self.x > other.x 41 42 # 1. 算數運算符 43 v01 = Vector(10) 44 # 自定義向量 + 整數 --> 向量 45 print(v01 + 5) # v01.__add__(5) 46 # 自定義向量 + 自定義向量 --> 向量 47 v02 = Vector(2) 48 print(v01 + v02) 49 50 # 2. 反向算數運算符 51 # 整數 - 自定義向量 --> 向量 52 print(5 - v01) # v01.__rsub__(5) 53 54 """ 55 list01 = [1] 56 57 # print(id(list01)) 58 list01 += [2]# 在原有基礎上增長新元素 59 # print(id(list01)) 60 61 # print(id(list01)) 62 list01 = list01 + [3]# 產生新的對象 63 # print(id(list01)) 64 65 print(list01) 66 """ 67 68 # 3. 複合運算符 69 # 默認使用算數運算符 70 print(id(v02)) 71 v02 += 3 # v02 = v02 + 3 72 print(v02) 73 print(id(v02)) 74 75 # 4. 比較運算符 76 # 自定義類 > 自定義類 77 print(v01 > v02)# v01.__gt__(v02)
Open Closed Principle
對擴展開放,對修改關閉。
增長新功能,不改變原有代碼。
Single Responsibility Principle
一個類有且只有一個改變它的緣由。
Dependency Inversion Principle
客戶端代碼(調用的類)儘可能依賴(使用)抽象。
抽象不該該依賴細節,細節應該依賴抽象。
Composite Reuse Principle
若是僅僅爲了代碼複用優先選擇組合複用,而非繼承複用。
組合的耦合性相對繼承低。
Liskov Substitution Principle
父類出現的地方能夠被子類替換,在替換後依然保持原功能。
子類要擁有父類的全部功能。
子類在重寫父類方法時,儘可能選擇擴展重寫,防止改變了功能。在Ctrl+o時,別刪東西,重寫後擴展重寫
Law of Demeter
不要和陌生人說話。
類與類交互時,在知足功能要求的基礎上,傳遞的數據量越少越好。由於這樣可能下降耦合度(利用抽象去調用子類,隔離變化)。
1 """ 2 面向對象的應用: 3 定義員工管理器,記錄全部員工,計算總工資. 4 程序員:底薪 + 項目分成 5 測試員:底薪 + bug * 5 6 要求:增長新的崗位,不影響員工管理器. 7 指出:三大特徵,六個原則. 8 9 封裝:員工管理器,程序員,測試員 10 繼承:員工隔離員工管理器與具體員工的變化 11 多態:員工管理器調用員工,執行程序員,測試員. 12 開閉:增長新的崗位,不影響員工管理器. 13 單一:員工管理器(管理員工),員工(隔離變化),程序員(計算薪資),測試員(計算薪資) 14 依賴倒置:員工管理器調用員工,而不調用程序員/測試員 15 組合複用:員工管理器存儲具體員工的變量
里氏替換:程序員(繼承員工底薪後添加獎金),測試員(繼承員工底薪後添加bug提成)
迪米特法則:員工管理器與程序員,測試員的交互,改由與員工交互,減小了數據的傳遞
16 """ 17 18 19 class EmployeeManager: 20 """ 21 員工管理器 22 """ 23 24 def __init__(self): 25 self.__all_employee = [] 26 27 def add_employee(self, emp): 28 # 判斷 emp 是員工 則 添加....? 29 # if type(emp) == Employee: 30 # emp 屬於 員工 31 if isinstance(emp, Employee): 32 self.__all_employee.append(emp) 33 34 def get_total_salary(self): 35 total_sarlay = 0 36 for itme in self.__all_employee: 37 # 調用的是員工 38 # 執行的是程序員/測試員 39 total_sarlay += itme.calculate_salary() 40 return total_sarlay 41 42 43 class Employee: 44 """ 45 員工 46 抽象的 --> 統一律念 --> 隔離變化 47 48 """ 49 50 def __init__(self, base_salary): 51 self.base_salary = base_salary 52 53 def calculate_salary(self): 54 """ 55 計算薪資 56 :return: 小數類型的薪資 57 """ 58 return self.base_salary 59 60 61 class Programmer(Employee): 62 """ 63 程序員 64 """ 65 66 def __init__(self, base_salary=0, bonus=0): 67 super().__init__(base_salary) 68 self.bonus = bonus 69 70 def calculate_salary(self): 71 # return self.base_salary + self.bonus 72 return super().calculate_salary() + self.bonus 73 74 75 class Tester(Employee): 76 def __init__(self, base_salary=0, bug_count=0): 77 super().__init__(base_salary) 78 self.bug_count = bug_count 79 80 def calculate_salary(self): 81 return super().calculate_salary() * self.bug_count * 5 82 83 # def calculate_salary(self): 84 # return self.base_salary * self.bug_count * 5 85 86 manager = EmployeeManager() 87 p01 = Programmer(32000, 50000) 88 manager.add_employee(p01) 89 manager.add_employee(Tester(8000, 2)) 90 print(manager.get_total_salary())