python设计模式总结
介紹
每一個模式描述了一個在我們周圍不斷重復發生的問題,以及該問題的解決方案的核心。這樣你就能一次又一次地使用該方案而不必做重復勞動。—— Christopher Alexander
軟件危機催生了設計模式,面向對象,軟件工程。
是什么
每一個設計模式系統地命名、解釋和評價了面向對象系統中一個重要的和重復出現的設計。
熱身
既然是針對面向對象的,讓我們先來回顧一下面向的三大特性和接口
面向對象的三大特性:封裝,繼承和多態
封裝
把數據和函數包裝在類里,類的邊界限制外界的訪問,將類內和類外隔絕開,并視情況提供一些接口。
尤其是一些關鍵的變量(比如在類內部計算出來的變量),建議設置為私有變量,隔絕類外的訪問和修改。
class A:def __init__(self, name):self.__name = namedef getname(self): # 接口return self.__namedef setname(self, name): # 接口self.__name = nameclass B(A):def __init__(self):print(self.__name)b = B() # 報錯私有變量只有類內才能訪問,類外不能,親兒子也不可以!
這里,我們讓B繼承A,實例化,得到以下出錯信息。這是因為私有變量在定義階段就會發生變形,原理復習這里
AttributeError: ‘B’ object has no attribute ‘_B__name’
繼承和多態
繼承是為了實現代碼復用,并且子類可以重寫父類的方法或者派生自己的屬性。Python語言本身就是多態的,程序員不需要關心。
接口:
接口是一種特殊的類,聲明了若干方法,要求繼承該接口的類必須實現這些方法。
作用:限制繼承接口的類的方法的名稱及調用方式;隱藏了類的內部實現。
python中實現接口有兩種方式:抽象類和 raise NotImplemented
# 方式一:父類中定義方法,應用raise NotImplemented約束 class BaseClient(object):def __init__(self):self.api = settings.APIdef execute(self):raise NotImplemented("子類必須實現execute方法")class AgentClient(BaseClient): #子類必須實現raise NotImplemented約束的方法def execute(self):obj = Plugin()res = obj.execute_plugin()self.post_data(res)# 方式二:抽象類 from abc import abstractmethod, ABCMetaclass Payment(metaclass=ABCMeta):@abstractmethoddef pay(self, money):"""支付方法,參數money"""pass#實現Payment接口 class Alipay(Payment):def pay(self, money):print("支付寶支付%s元"%money)class ApplePay(Payment):def pay(self, money):print("蘋果支付%s元"%money)設計模式六大原則
開閉原則
一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。即軟件實體應盡量在不修改原有代碼的情況下進行擴展。
里氏(Liskov)替換原則
所有引用基類(父類)的地方必須能透明地使用其子類的對象。
# 如果我在下面的高層代碼def func中引用了基類User的對象,那么我一定也可以傳入User子類VIPUser的對象,不論子類是繼承還是重寫了父類中的方法。 class User:def check(self):print('checking')return Trueclass VIPUser(User):def check(self):print('vip-chating')return Truedef chat(self):print('Chatting')def func(user):res = user.check()依賴倒置原則
高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程(需求提出后,先定義接口,作為約束,以后不管底層實現如何變,也不會影響到高層)。
接口隔離原則
使用多個專門的接口,而不使用單一的總接口,即客戶端不應該依賴那些它不需要的接口。
class Animal(metaclass=ABCMeta):@abstractmethoddef walk(self):pass@abstractmethoddef swim(self):pass@abstractmethoddef fly(self):passclass Tiger(Animal):def walk(self):print("Tiger Walk")def swim(self):print("Tiger Walk")def fly(self):print("Tiger Walk")以上肯定不合理,老虎會飛嗎?因此fly這個方法Tiger類根不不需要,但是接口卻約束子類必須實現。正確的應該是定義三個動物類,讓子類進行多繼承:
class LandAnimal(metaclass=ABCMeta):@abstractmethoddef walk(self):passclass SkyAnimal(metaclass=ABCMeta):@abstractmethoddef fly(self):passclass WaterAnimal(metaclass=ABCMeta):@abstractmethoddef swim(self):passclass Tiger(LandAnimal):def walk(self):print("Tiger Walk")class Frog(WaterAnimal, LandAnimal):def walk(self):print('Frog Walk')def swim(self):print('Frog swim')迪米特法則
一個軟件實體應當盡可能少地與其他實體發生相互作用,也就是解耦。
單一職責原則
不要存在多于一個導致類變更的原因。通俗的說,即一個類只負責一項職責。
設計模式分類
創建型模式
簡單工廠模式
內容
不直接向客戶端暴露對象創建的實現細節,而是通過一個工廠類來負責創建產品類的實例。工廠類屬于底層代碼,客戶端指的是高層代碼。
角色
- 工廠角色(Creator),隱藏對象創建的細節
- 抽象產品角色(Product),定義接口
- 具體產品角色(Concrete Product),實現接口,。
優點
- 隱藏了對象創建的實現細節;
- 客戶端不需要修改代碼
缺點:
- 違反了單一職責原則,將創建邏輯集中到一個工廠類里;
- 當添加新產品時,需要修改工廠類代碼,違反了開閉原則
工廠方法模式
內容
定義一個用于創建對象的接口(工廠接口),讓子類決定實例化哪一個產品類。
角色
- 抽象工廠角色(Creator),定義工廠類接口
- 具體工廠角色(Concrete Creator),隱藏對象創建細節
- 抽象產品角色(Product),定義產品接口
- 具體產品角色(Concrete Product),實現產品接口
特點
工廠方法模式相比簡單工廠模式將每個具體產品都對應了一個具體工廠。
適用場景
- 需要生產多種、大量復雜對象的時候
- 需要降低耦合度的時候
- 當系統中的產品種類需要經常擴展的時候
優點
- 每個具體產品都對應一個具體工廠類,不需要修改工廠類代碼
- 隱藏了對象創建的實現細節
缺點
- 每增加一個具體產品類,就必須增加一個相應的具體工廠類
抽象工廠模式
內容
定義一個工廠類接口,讓工廠子類來創建一系列相關或相互依賴的對象。例:生產一部手機,需要手機殼、CPU、操作系統三類對象進行組裝,其中每類對象都有不同的種類。對每個具體工廠,分別生產一部手機所需要的三個對象。
角色
- 抽象工廠角色(Creator)
- 具體工廠角色(Concrete Creator)
- 抽象產品角色(Product)
- 具體產品角色(Concrete Product)
- 客戶端(Client)
特點
相比工廠方法模式,抽象工廠模式中的每個具體工廠都生產一套產品。該模式只負責生成,組裝由客戶端負責
使用場景
- 系統要獨立于產品的創建與組合時
- 強調一系列相關的產品對象的設計以便進行聯合使用時
- 提供一個產品類庫,想隱藏產品的具體實現時
優點
- 將客戶端與類的具體實現相分離
- 每個工廠創建了一個完整的產品系列,使得易于交換產品系列
- 有利于產品的一致性(即產品之間的約束關系)
缺點
- 難以支持新種類的(抽象)產品。比如加一個電池,所有都要大改。
創建者模式
內容
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
角色
- 抽象建造者(Builder)
- 具體建造者(Concrete Builder),隱藏內部結構
- 指揮者(Director),隱藏裝配過程
- 產品(Product)
特點
建造者模式與抽象工廠模式相似,也用來創建復雜對象。主要區別是建造者模式著重一步步構造一個復雜對象,而抽象工廠模式著重于多個系列的產品對象。
使用場景
- 當創建復雜對象的算法(Director)應該獨立于該對象的組成部分以及它們的裝配方式(Builder)時
- 當構造過程允許被構造的對象有不同的表示時(不同Builder)。
優點
- 隱藏了一個產品的內部結構和裝配過程
- 將構造代碼與表示代碼分開
- 可以對構造過程進行更精細的控制
原型模式
暫不討論,略
單例模式
內容
保證一個類只有一個實例,并提供一個訪問它的全局訪問點。
角色
- 單例(Singleton)
適用場景
- 當類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時
優點
- 對唯一實例的受控訪問
- 單例相當于全局變量,但防止了命名空間被污染
其它
與單例模式功能相似的概念:全局變量、靜態變量(方法)
單例模式的幾種實現方式
方式一:__new__
這里先了解下__new__和__init__的區別:感謝這位同學的整理
class A(object):def __init__(self):print "init"def __new__(cls,*args, **kwargs):print "new %s"%clsreturn object.__new__(cls, *args, **kwargs)A() """ new <class '__main__.A'> init """幾點說明:
通過__new__實現單例模式:
class Singleton(object):def __new__(cls, *args, **kwargs):# 如果當前類沒有_instance屬性,那么就調用父類的__new__方法實例化對象,新增_instance屬性并賦值if not hasattr(cls, "_instance"): cls._instance = super(Singleton, cls).__new__(cls)return cls._instanceclass MyClass(Singleton):def __init__(self, name):self.name = namea = MyClass("a") print(a) # <__main__.MyClass object at 0x0000025D076AD3C8> print(a.name) # ab = MyClass('b') print(b) # <__main__.MyClass object at 0x0000025D076AD3C8> print(b.name) # bb.name = 'xxx' print(a) # <__main__.MyClass object at 0x0000025D076AD3C8> print(a.name) # xxx方式二:通過裝飾器
在裝飾器中,我們通過攔截類的__new__實現,判斷該類是否存在于__dict__字典中,如果存在則返回該類的實例,不存在則實例化該類并且存放于__dict__中。
# 方式一: def singleton(cls, *args, **kw):instances = {}def getinstance():if cls not in instances:instances[cls] = cls(*args, **kw)return instances[cls]return getinstance@singleton class myclass:passclass1 = myclass() class2 = myclass()assert class1 == class2# 方式二:和上面裝飾器思路是一樣的。只是寫到類方法里。 class Foo:_instance = Nonedef __init__(self):pass@classmethoddef get_instance(cls):if cls._instance:return cls._instanceelse:obj = cls()cls._instance = objreturn obj# 創建實例 obj = Foo.get_instance()方式三:文件單例模式:
# step1: 在一個py文件中定義一個單例類并實例化 class Singleton(object):def __init__(self):passdef foo(self):passdef bar(self):passinstance = Singleton()# step2: 在其它文件中導入實例化對象,每次都是操作同一個實例 from Singleton import instanceinstance.foo() instance.bar()文件單例模式的特點:
創建模式小結
三種工廠模式:
- 簡單工廠模式:只有一個工廠
- 工廠方法模式:一個產品一個工廠
- 抽象工廠模式:一套產品一個工廠
結構型模式
適配器模式
內容
將一個類的接口轉換成客戶希望的另一個接口。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。就單來說就是實現接口一致和代碼復用,如果接口不一致,寫一個轉接頭。
角色
- 目標接口(Target)
- 待適配的類(Adaptee)
- 適配器(Adapter)
兩種實現方式
- 類適配器:使用多繼承
- 對象適配器:使用組合
適用場景
- 想使用一個已經存在的類,而它的接口不符合要求
- (對象適配器)想使用一些已經存在的子類,但不可能對每一個都進行子類化以匹配它們的接口。對象適配器可以適配它的父類接口。
注意:
橋模式
暫不討論。
組合模式
內容
將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。其實就是用設計模式的方式來表達樹形結構,一個節點是一個部分,一個子樹是一個整體。
角色
- 抽象組件(Component):抽象組件中定義的方法,葉子和復合組件都要實現
- 葉子組件(Leaf)
- 復合組件(Composite):非葉子組件
- 客戶端(Client)
適用場景
- 表示對象的“部分-整體”層次結構(特別是結構是遞歸的)
- 希望用戶忽略組合對象與單個對象的不同,用戶統一地使用組合結構中的所有對象
優點
- 定義了包含基本對象和組合對象的類層次結構
- 簡化客戶端代碼,即客戶端可以一致地使用組合對象和單個對象
- 更容易增加新類型的組件
代碼示例
# coding : utf-8 # create by ztypl on 2017/5/25from abc import abstractmethod, ABCMeta# 抽象組件 class Graphic(metaclass=ABCMeta):@abstractmethoddef draw(self):pass@abstractmethoddef add(self, graphic):passdef getchildren(self):pass# 圖元 # 葉子組件 class Point(Graphic):def __init__(self, x, y):self.x = xself.y = ydef draw(self):print(self)def add(self, graphic):raise TypeErrordef getchildren(self):raise TypeErrordef __str__(self):return "點(%s, %s)" % (self.x, self.y)# 葉子組件 class Line(Graphic):def __init__(self, p1, p2):self.p1 = p1self.p2 = p2def draw(self):print(self)def add(self, graphic):raise TypeErrordef getchildren(self):raise TypeErrordef __str__(self):return "線段[%s, %s]" % (self.p1, self.p2)# 復合組件 class Picture(Graphic):def __init__(self):self.children = []def add(self, graphic):self.children.append(graphic)def getchildren(self):return self.childrendef draw(self):print("------復合圖形------")for g in self.children:g.draw()print("------END------")pic1 = Picture() point = Point(2,3) pic1.add(point) pic1.add(Line(Point(1,2), Point(4,5))) pic1.add(Line(Point(0,1), Point(2,1)))pic2 = Picture() pic2.add(Point(-2,-1)) pic2.add(Line(Point(0,0), Point(1,1)))pic = Picture() pic.add(pic1) pic.add(pic2)pic.draw() # pic1.draw() # point.draw()裝飾模式
暫不討論。
外觀模式
暫不討論。
享元模式
暫不討論
代理模式
內容
為其他對象提供一種代理以控制對這個對象的訪問。
角色
- 抽象實體(Subject)
- 實體(RealSubject)
- 代理(Proxy)
使用場景
- 遠程代理:為遠程的對象提供代理。
- 理虛代理:根據需要創建很大的對象。
- 保護代理:控制對原始對象的訪問,用于對象有不同訪問權限時
優點
- 遠程代理:可以隱藏對象位于遠程地址空間的事實。高層代碼不需要親自訪問遠程的對象,也不需要知道對象在本地還是遠程
- 虛代理:可以進行優化,例如根據要求創建對象。比如手機滑屏,未顯示區域會提前加載,但是圖像又很耗費內存,因此加載的是圖像代理,當到達某個臨界值,才加載真實圖像。再比如瀏覽器的無圖模式,點擊時才創建圖片。
- 保護代理:允許在訪問一個對象時有一些附加的內務處理
示例代碼
# coding : utf-8 # create by ztypl on 2017/5/26from abc import ABCMeta, abstractmethodclass Subject(metaclass=ABCMeta):@abstractmethoddef get_content(self):passdef set_content(self, content):passclass RealSubject(Subject):def __init__(self, filename):self.filename = filenameprint("讀取%s文件內容"%filename)f = open(filename)self.__content = f.read()f.close()def get_content(self):return self.__contentdef set_content(self, content):f = open(self.filename, 'w')f.write(content)self.__content = contentf.close()# ProxyA 就是給真實對象套了一個馬甲,和使用真實對象沒區別 class ProxyA(Subject):def __init__(self, filename):self.subj = RealSubject(filename)def get_content(self):return self.subj.get_content()def set_content(self, content):return self.subj.set_content(content)# 虛代理:創建時不會立即加載真實對象,而是在調用后才加載(手機的無圖模式就是這個原理) class ProxyB(Subject):def __init__(self, filename):self.filename = filenameself.subj = Nonedef get_content(self):if not self.subj:self.subj = RealSubject(self.filename)return self.subj.get_content()x = ProxyB('abc.txt') #print(x.get_content())# 保護代理:控制對真實對象的修改 class ProxyC(Subject):def __init__(self, filename):self.subj = RealSubject(filename)def get_content(self):self.subj.get_content()def set_content(self, content):raise PermissionError# filename = "abc.txt" # username = input() # if username!="admin": # p = ProxyC(filename) # else: # p = ProxyA(filename) # # print(p.get_content())行為型模式
解釋器模式
暫不討論。
責任鏈模式
內容
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
角色
- 抽象處理者(Handler)
- 具體處理者(ConcreteHandler)
- 客戶端(Client)
適用場景
- 有多個對象可以處理一個請求,哪個對象處理由運行時決定
- 在不明確接收者的情況下,向多個對象中的一個提交一個請求
優點
- 降低耦合度:一個對象無需知道是其他哪一個對象處理其請求
示例代碼:請假批準
from abc import ABCMeta, abstractmethodclass Handler(metaclass=ABCMeta):@abstractmethoddef handle_leave(self, day):passclass GeneralManagerHandler(Handler):def handle_leave(self, day):if day < 10:print("總經理批準%d天假"%day)return Trueelse:print("呵呵")return Falseclass DepartmentManagerHandler(Handler):def __init__(self):self.superior = GeneralManagerHandler()def handle_leave(self, day):if day < 7:print("部門經理批準%d天假"%day)return Trueelse:print("部門經理無權準假")return self.superior.handle_leave(day)class ProjectDirectorHandler(Handler):def __init__(self):self.superior = DepartmentManagerHandler()def handle_leave(self, day):if day < 3:print("項目主管批準%d天假"%day)return Trueelse:print("項目主管無權準假")return self.superior.handle_leave(day)# day = 11 # h = ProjectDirectorHandler() # print(h.handle_leave(day))示例代碼:Javascript事件浮升機制
JS事件浮生(冒泡)復習:http://blog.csdn.net/ayhan_huang/article/details/78629885
在下面這個示例中,DOM節點的父元素就相當與責任鏈的下一級
class Handler(metaclass=ABCMeta):@abstractmethoddef add_event(self, func):pass@abstractmethoddef handle(self):passclass BodyHandler(Handler):def __init__(self):self.func = Nonedef add_event(self, func):self.func = funcdef handle(self):if self.func:return self.func()else:print("已到最后一級,無法處理")class ElementHandler(Handler):def __init__(self, parent):self.func = Noneself.parent = parentdef add_event(self, func):self.func = funcdef handle(self):if self.func:return self.func()else:return self.parent.handle()# ---------- 客戶端代碼 -------------------# 定義元素并嵌套 <body><div><a> body = {'type': 'body', 'name': 'body', 'children': [], 'father': None} div = {'type': 'div', 'name': 'div', 'children': [], 'father': body} a = {'type': 'a', 'name': 'a', 'children': [], 'father': div} body['children'].append(div) div['children'].append(a)# 給各元素增加事件處理對象 body['event_handler'] = BodyHandler() div['event_handler'] = ElementHandler(div['father']['event_handler']) a['event_handler'] = ElementHandler(a['father']['event_handler'])# 為元素綁定事件 def attach_event(element, func):element['event_handler'].add_event(func)# 具體事件 def func_div():print("這是給div的函數")def func_a():print("這是給a的函數")def func_body():print("這是給body的函數")attach_event(div, func_div) # attach_event(a, func_a) attach_event(body, func_body)# 執行a的事件,a沒有,那么上浮,執行父元素div的事件 a['event_handler'].handle()命令模式
暫不討論。
迭代器模式
內容
提供一種方法順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示
實現方法
通過__iter__, __next__ 方法,可以實現一個迭代器
示例
下面用到了鏈表這種數據結構,鏈表的知識參考這里:http://blog.csdn.net/ayhan_huang/article/details/78526442
class LinkList:"""鏈表 頭結點保存鏈表的長度"""class Node:# 節點def __init__(self, item=None):self.item = itemself.next = Noneclass LinkListIterator:# 手動實現一個鏈表迭代器def __init__(self, node):self.node = nodedef __next__(self):if self.node:cur_node = self.nodeself.node = cur_node.nextreturn cur_node.itemelse:raise StopIterationdef __iter__(self):return selfdef __init__(self, iterable=None):self.head = LinkList.Node(0) # 初始化頭結點self.tail = self.headself.extend(iterable)def append(self, obj): # 添加節點(尾插法)s = LinkList.Node(obj)self.tail.next = sself.tail = sself.head.item += 1 # 鏈表長度+1def extend(self, iterable):for obj in iterable:self.append(obj)def __iter__(self):return self.LinkListIterator(self.head.next) # 從頭節點的下一個開始迭代def __len__(self):return self.head.itemdef __str__(self):return "<<" + ", ".join(map(str, self)) + ">>"li = [i for i in range(10)] lk = LinkList(li) print(lk) # <<0, 1, 2, 3, 4, 5, 6, 7, 8, 9>>中介者模式
暫不討論。
備忘錄模式
暫不討論。
觀察者模式
內容
定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時, 所有依賴于它的對象都得到通知并被自動更新。觀察者模式又稱“發布-訂閱”模式。
比如,我們根據excel表格數據生成餅狀圖、柱狀圖、折線圖,一旦我們更新了excel表格數據,那么這些圖表也會隨之更新。這些圖表就是觀察者。
角色
- 抽象主體(Subject)
- 具體主體(ConcreteSubject)——發布者
- 抽象觀察者(Observer)
- 具體觀察者(ConcreteObserver)——訂閱者
適用場景
- 當一個抽象模型有兩方面,其中一個方面依賴于另一個方面。將這兩者封裝在獨立對象中以使它們可以各自獨立地改變和復用
- 當對一個對象的改變需要同時改變其它對象,而不知道具體有多少對象有待改變
- 當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之,你不希望這些對象是緊密耦合的。
優點
- 目標和觀察者之間的抽象耦合最小
- 支持廣播通信
示例
from abc import ABCMeta, abstractmethod# 抽象觀察者/訂閱者 class Observer(metaclass=ABCMeta):@abstractmethoddef update(self, notice):pass# 通知 基類 class Notification(object):def __init__(self):self.observers = []def attach(self, obs): # 增加觀察者self.observers.append(obs)def detach(self, obs): # 移除觀察者self.observers.remove(obs)def notify(self): # 通知/廣播for obj in self.observers:obj.update(self)# 具體的天氣通知 class WeatherNotifications(Notification):def __init__(self, weather_info=None):super().__init__()self.__weather_info = weather_infodef detach(self, obs):super().detach(obs)# obs.__weather_info = None@propertydef weather_info(self):return self.__weather_info@weather_info.setterdef weather_info(self, weather_info):self.__weather_info = weather_infoself.notify() # 自動傳入消息對象self# 具體觀察者/訂閱者 class Subscriber(Observer):def __init__(self):self.weather_info = Nonedef update(self, notification):self.weather_info = notification.weather_info# 實例化天氣消息發布者 notice = WeatherNotifications()# 實例化訂閱者 Lena = Subscriber() Ayhan = Subscriber()# 為天氣消息發布者添加訂閱對象 notice.attach(Lena) notice.attach(Ayhan)# 發布天氣消息 notice.weather_info = '今夜陽光明媚' # 訂閱者收到消息 print(Lena.weather_info) # 今夜陽光明媚 print(Ayhan.weather_info) # 今夜陽光明媚# 移除訂閱者 notice.detach(Ayhan) # 發布最新消息 notice.weather_info = '暴風雨即將來臨' print(Lena.weather_info) # 暴風雨即將來臨 print(Ayhan.weather_info) # 今夜陽光明媚狀態模式
暫不討論。
策略模式
內容
定義一系列的算法,把它們一個個封裝起來,并且使它們可相互替換。本模式使得算法可獨立于使用它的客戶而變化。
角色
- 抽象策略(Strategy)
- 具體策略(ConcreteStrategy)
- 上下文(Context):連接策略類和高層代碼,可以選擇策略。比如打車時,高峰期和平時,應該選擇不同的策略。
適用場景
- 許多相關的類僅僅是行為有異
- 需要使用一個算法的不同變體
- 算法使用了客戶端無需知道的數據。比如算法中需要當前時間,那么在Context中自動生成傳入。
- 一個類中的多種行為以多個條件語句的形式存在,可以將這些行為封裝在不同的策略類中。
優點
- 定義了一系列可重用的算法和行為
- 消除了一些條件語句
- 可以提供相同行為的不同實現
缺點
- 客戶必須了解不同的策略
示例
客戶端可以根據需要,選擇排序或歸并策略。
Python 排序算法參考:http://blog.csdn.net/ayhan_huang/article/details/78564600
from abc import ABCMeta, abstractmethod import randomclass Sort(metaclass=ABCMeta):@abstractmethoddef sort(self, data):pass# 快排策略 class QuickSort(Sort):def quick_sort(self, data, left, right):if left < right:mid = self.partition(data, left, right)self.quick_sort(data, left, mid - 1)self.quick_sort(data, mid + 1, right)def partition(self, data, left, right):tmp = data[left]while left < right:while left < right and data[right] >= tmp:right -= 1data[left] = data[right]while left < right and data[left] <= tmp:left += 1data[right] = data[left]data[left] = tmpreturn leftdef sort(self, data):print("快速排序")return self.quick_sort(data, 0, len(data) - 1)# 歸并策略 class MergeSort(Sort):def merge(self, data, low, mid, high):i = lowj = mid + 1ltmp = []while i <= mid and j <= high:if data[i] <= data[j]:ltmp.append(data[i])i += 1else:ltmp.append(data[j])j += 1while i <= mid:ltmp.append(data[i])i += 1while j <= high:ltmp.append(data[j])j += 1data[low:high + 1] = ltmpdef merge_sort(self, data, low, high):if low < high:mid = (low + high) // 2self.merge_sort(data, low, mid)self.merge_sort(data, mid + 1, high)self.merge(data, low, mid, high)def sort(self, data):print("歸并排序")return self.merge_sort(data, 0, len(data) - 1)class Context:def __init__(self, data, strategy=None):self.data = dataself.strategy = strategydef set_strategy(self, strategy):self.strategy = strategydef do_strategy(self):if self.strategy:self.strategy.sort(self.data)else:raise TypeErrorli = list(range(100000)) random.shuffle(li)context = Context(li, MergeSort()) context.do_strategy()random.shuffle(context.data)context.set_strategy(QuickSort()) context.do_strategy()訪問者模式
暫不討論。
模板方法模式
內容
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
角色
- 抽象類(AbstractClass):定義抽象的原子操作(鉤子操作);實現一個模板方法作為算法的骨架
- 具體類(ConcreteClass):實現原子操作
適用場景
- 一次性實現一個算法的不變的部分
- 各個子類中的公共行為應該被提取出來并集中到一個公共父類中以避免代碼重復
- 控制子類擴展
示例
以下以文件處理為例,打開 - 處理 - 關閉 這三步就是不可分割的原子操作。
# coding : utf-8 # create by ztypl on 2017/5/27from abc import ABCMeta, abstractmethodclass IOHandler(metaclass=ABCMeta):@abstractmethoddef open(self, name):pass@abstractmethoddef deal(self, change):pass@abstractmethoddef close(self):passdef process(self, name, change):self.open(name)self.deal(change)self.close()class FileHandler(IOHandler):def open(self, name):self.file = open(name,"w")def deal(self, change):self.file.write(change)def close(self):self.file.close()f = FileHandler() f.process("abc.txt", "Hello World")總結
以上是生活随笔為你收集整理的python设计模式总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宣布 Windows Azure 通过
- 下一篇: AJAX学习笔记(基本使用,请求参数传递