一. 什么是python的設計模式?
軟件工程中,設計模式是指軟件設計問題的推薦方案。設計模式一般是描述如何組織代碼和使用最佳實踐來解決常見的設計問題。需謹記一點:設計模式是高層次的方案,並不關注具體的實現細節,比如算法和數據結構。對於正在嘗試解決的問題,何種算法和數據結構最優,則是由軟件工程自己把握。面試經常會問到設計模式,所以我給大家准備一些常用的設計模式,也可以更好的與面試官交流。
二. python實現設計模式
設計模式共分為三大類,細分為23種設計模式。
- 創建型模式
- 結構型模式
- 行為型模式
2.1創建型模式
單例模式
單例模式(Singleton Pattern)是一個常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在,當希望在某個系統中只出現一個實例時,單例對象就能派上用場。比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。
class Singleton(): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls) return cls._instance s1 = Singleton() s2 = Singleton() print(id(s1),id(s2))
工廠模式
工廠模式是一個在軟件開發中用來創建對象的設計模式。
在工廠模式設計中,客戶端可以請求一個對象,而無需知道這個對象來自哪里(使用類來生成對象)也就是說,客戶只需要調用指定的函數或者方法,傳入具有辨別性質的參數(如下面代碼的"M","F")得到的返回值就是最終的對象。工廠背后的思想是簡化對象的創建,基於一個中心化的函數(定義一個抽象的實例化對象的接口)來實現創建對象,(類似於我在面向對象的類的約束中講過的歸一化設計)通過將創建對象的代碼和使用對象的代碼解耦,工廠能夠降低應用維護的復雜度。
class Person: def __init__(self, name, gender): self.name = name self.gender = gender def get_name(self): return self.name def get_gender(self): return self.gender class Male(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Mr." + self.name) class Female(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Miss." + name) if __name__ == '__main__': obj1 = Male('太白','male') obj3 = Male('瑋哥', 'male') obj2 = Female('小花', 'female') print(obj1.get_name()) print(obj1.get_gender()) print(obj2.get_gender()) print(obj3.get_gender())
class Person: def __init__(self, name, gender): self.name = name self.gender = gender def get_name(self): return self.name def get_gender(self): return self.gender class Male(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Mr." + self.name) class Female(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Miss." + name) class Factory: def get_person(self, name, gender): # 統一了實例化對象的接口 if gender == 'male': return Male(name, gender) else: return Female(name, gender) if __name__ == '__main__': factory = Factory() # 每次實例化對象時調用統一的接口即可 p1 = factory.get_person("太白", "male") p2 = factory.get_person("小花", "female") p3 = factory.get_person("瑋哥", "male") p1.get_name() p1.get_gender() p2.get_name() p2.get_gender() p3.get_name()
建造者模式
將一個復雜對象的構建與他的表示分離,使得同樣的構建過程可以創建不同的表示。在該模式中,有兩個參與者:建造者(builder)和指揮者(director)。建造者負責創建復雜對象的各個組成部分,指揮者負責制定對象特定的執行順序。
示例:
1.有一個接口類,定義創建對象的方法。一個指揮員類,接受創造者對象為參數(組合)。兩個創造者類,創建對象方法相同,內部創建可自定義
2.一個指揮員,兩個創造者(瘦子 胖子),指揮員可以指定由哪個創造者來創造。
from abc import ABCMeta, abstractmethod class Builder(metaclass=ABCMeta): # 接口類,制定規范,只要此類必須要設置以下的被abstractmethod裝飾的這些方法。 @abstractmethod def draw_arm(self): pass @abstractmethod def draw_foot(self): pass @abstractmethod def draw_head(self): pass @abstractmethod def draw_body(self): pass class Thin(Builder): def draw_arm(self): print("畫手") def draw_foot(self): print("畫腳") def draw_head(self): print("畫頭") def draw_body(self): print("畫瘦身體") class Fat(Builder): def draw_arm(self): print("畫手") def draw_foot(self): print("畫腳") def draw_head(self): print("畫頭") def draw_body(self): print("畫胖身體") class Director(): def __init__(self, person): self.person = person def draw(self): self.person.draw_arm() self.person.draw_foot() self.person.draw_head() self.person.draw_body() if __name__ == '__main__': thin = Thin() fat = Fat() director_thin = Director(thin) # 組合 director_thin.draw() # 按照指揮者的類的draw方法順序執行 director_fat = Director(fat) # 組合 director_fat.draw() # 按照指揮者的類的draw方法順序執行
原型模式
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
原型模式本質是克隆對象,所以在對象初始化操作比較復雜的情況下,很實用,能大大降低耗時,提高性能。因為“不用重新初始化對象,而是動態獲得對象運行的狀態”。屬於副本的概念,與引用不同。
引用就是多個變量共同指向一個數據的內存地址(變量多次賦值)。副本就是重新創建一份。
原型模式示例:某一本書在初版之后又經歷了一次改版,對書名、價錢、頁碼等進行了修改。但是這次修改並不是重新裝訂了一本書,只是修改了部分內容,這時候就不需要重新創建一個對象,只需要把原來的對象的副本內容做一些修改。
import copy from collections import OrderedDict class Book: def __init__(self, name, authors, price, **rest): '''rest的例子有:出版商、長度、標簽、出版日期''' self.name = name self.authors = authors self.price = price # 單位為美元 self.__dict__.update(rest) def __str__(self): return ''.join([f'{k}: {v}\n' if k != "price" else f'{k}: {v}$\n' for k, v in self.__dict__.items()]) class Prototype: def __init__(self): self.objects = dict() def register(self, identifier, obj): self.objects[identifier] = obj def unregister(self, identifier): del self.objects[identifier] def clone(self, identifier, **attr): found = self.objects.get(identifier) if not found: raise ValueError('Incorrect object identifier: {}'.format(identifier)) obj = copy.deepcopy(found) # 深copy一個對象 obj.__dict__.update(attr) return obj def main(): b1 = Book('太白教你學python', ('太白金星', '老司機'), price=118, publisher='IT出版社', publication_date='2014-08-22', tags=('IT', 'programming', 'py', '人工智能')) prototype = Prototype() cid = 'first' prototype.register(cid, b1) b2 = prototype.clone(cid, name='Django框架深度剖析', price=129.9, publication_date='2016-04-01', edition=2) # 通過克隆b1對象的方式創建b2對象,這樣避免了實例化對象b1時的復雜操作,提升性能,很實用。 for i in (b1, b2): print(i) print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2))) if __name__ == '__main__': main()
