1. 程式人生 > 實用技巧 >python設計模式之模版方法設計模式

python設計模式之模版方法設計模式

  我們在使用python的flask框架時,可能會經常用到生命週期函式如:before_request, before_first_request,或者訊號等,剛開始學的時候就想只要寫一個函式,然後加上一個裝飾器居然就可以實現這種開掛般的效果,那時感覺這框架程式碼寫得真棒, 再過些時間自己學會閱讀框架原始碼時,在flask原始碼中的wsgi_app函式裡面發現了奧祕,原來是這樣寫就能實現插入生命週期的效果啊,時間在走知識在漲,不知不覺走進了設計模式的天堂,再猛然看flask框架原始碼的時候就覺得,原來如此,這不就是模版方法設計模式的具體應用嗎?接下來我們來看看什麼是模版方法設計模式來揭開它的神祕面紗。

  模版方法設計模式GOF官方的解釋是: 定義一個操作中的演演算法的骨架(穩定), 而將一些步驟(變化)延遲到子類中。 使得子類可以不改變(複用)一個演演算法的結構即可重定義該演演算法的某些特定步驟。

  模版方法設計模式的框架圖如下:

  可以看到在抽象類中定義了一系列固定流程的方法, 而在子類中去重寫或者實現具體的某些步驟。

  接下來我們用丐版的Flask來演示模版方法設計模式的精髓,宣告flask框架並不是這樣實現的,只是含有模版設計模式的思想, 我們的演示只是把這思想展示出來。

01、沒有用設計模式Flask

class Flask:

    def before_request(self):
pass def request(self):
pass def context(self):
print("我在儲存上下文") def response(self):
pass def clear(self):
print("我在清除上下文") class Application(Flask): def before_request(self):
print("我在煮飯前加了一個蛋") def request(self):
print("我正在吃飯") def response(self):
print("終於吃好了") def run(self):
self.before_request()
self.request()
self.context()
self.request()
self.clear() Application().run()

  我們發現,run方法執行的步驟是固定的,這樣每個app繼承Flask的時候都要實現一個run方法,加重了app開發者的負擔,因為run主程式的步驟是固定的,我們把run方法的實現移到抽象類Flask中,看一下效果。

02、 用了模版設計模式的Flask

class Flask:

    def before_request(self):
pass def request(self):
pass def context(self):
print("我在儲存上下文") def response(self):
pass def clear(self):
print("我在清除上下文") def run(self):
self.before_request()
self.request()
self.context()
self.request()
self.clear() class Application(Flask): def before_request(self):
print("我在煮飯前加了一個蛋") def request(self):
print("我正在吃飯") def response(self):
print("終於吃好了") Application().run()

  這裡我們就是把主程式run方法移動到抽象類Flask中,這時作為我們開發者,我們只要實現具體的步驟如,before_request和request等就可以了,這樣大大減輕了開發者負擔。

03、什麼時候使用模版方法設計模式

  在構建過程中,對於某一項任務,它通常有穩定的整體操作結構, 但各個子步驟卻有很多改變的需求,或者由於固有的原因(比如框架與應用之間的關係)而無法和任務的整體結構同時實現。在這個時候模版方法設計模式將會是你很好的一個選擇。

04、總結

  模版方法設計模式是一種非常基礎性的設計模式, 在面向物件系統中有大量的應用。它用最簡潔的機制(多型)為很多應用程式框架提供了靈活的擴充套件點,是程式碼複用方面的基本實現結構。

除了可以靈活應對子步驟的變化外, “不要呼叫我, 讓我來呼叫你”的反向控制結構是模版方法設計模式的典型應用。

最後還是奉上設計模式的8大基本設計原則:

  1. 依賴倒置原則(DIP)
  • 高層模組(穩定)不應該依賴於低層模組(變化),二者都應該依賴於抽象(穩定) 。
  • 抽象(穩定)不應該依賴於實現細節(變化) ,實現細節應該依賴於抽象(穩定)。
  1. 開放封閉原則(OCP)
  • 對擴充套件開放,對更改封閉。
  • 類模組應該是可擴充套件的,但是不可修改。
  1. 單一職責原則(SRP)
  • 一個類應該僅有一個引起它變化的原因。
  • 變化的方向隱含著類的責任。
  1. Liskov 替換原則(LSP)
  • 子類必須能夠替換它們的基類(IS-A)。
  • 繼承表達型別抽象。
  1. 介面隔離原則(ISP)
  • 不應該強迫客戶程式依賴它們不用的方法。
  • 介面應該小而完備。
  1. 優先使用物件組合,而不是類繼承
  • 類繼承通常為“白箱複用”,物件組合通常為“黑箱複用” 。
  • 繼承在某種程度上破壞了封裝性,子類父類耦合度高。
  • 而物件組合則只要求被組合的物件具有良好定義的介面,耦合度低。
  1. 封裝變化點
  • 使用封裝來建立物件之間的分界層,讓設計者可以在分界層的一側進行修改,而不會對另一側產生不良的影響,從而實現層次間的鬆耦合。
  1. 針對介面程式設計,而不是針對實現程式設計
  • 不將變數型別宣告為某個特定的具體類,而是宣告為某個介面。
  • 客戶程式無需獲知物件的具體型別,只需要知道物件所具有的介面。
  • 減少系統中各部分的依賴關係,從而實現“高內聚、鬆耦合”的型別設計方案