瞭解模板方法設計模式
模板方法模式適用於以下場景:
1、當多個算法或類實現類似或相同邏輯的時候
2、在子類中實現算法有助於減少重複代碼的時候
3、可以讓子類利用覆蓋實現行爲來定義多個算法的時候
模板方法模式的主要意圖:
1、使用基本操作定義算法的框架;
2、重新定義子類的某些操作,而無需修改算法的結構;
3、實現代碼重用並避免重複工作
4、利用通用接口或實現
模板方法模式使用以下術語——AbstractClass、ConcreteClass、TemplateMethod和Client;
1、AbstractClass:聲明一個定義算法步驟的接口
2、ConcreteClass:定義子類特定的步驟
3、template_method():通過調用步驟方法來定義算法
以下是一個代碼示例,展示了該模式中所有參與者的關係:
from abc import ABCMeta, abstractmethod
class AbstractClass(metaclass=ABCMeta):
def __init__(self):
pass
@abstractmethod
def operation1(self):
pass
@abstractmethod
def operation2(self):
pass
def template_method(self):
print("定義算法,operation1與operation2")
self.operation2()
self.operation1()
class ConcreteClass(AbstractClass):
def operation1(self):
print("My Concrete Operation1")
def operation2(self):
print("Operation2 remains same")
class Client(object):
def main(self):
self.concreate = ConcreteClass()
self.concreate.template_method()
client = Client()
client.main()
現在假設我們爲IOS設備開發自己的交叉編譯器並運行程序。
首先開發一個抽象類(編譯器),來定義編譯器的算法。編譯器執行的操作是收集由程序語言編寫的源代碼,然後編譯成目標代碼(二進制格式)。我們將這些步驟定義爲collect_source()和compile_to_object()抽象方法,同時還定義了負責執行程序的run()方法。該算法是由compile_and_run()方法來定義的,它通過內部調用collect_source()、compile_to_object()和run()方法來定義編譯器的算法。然後讓具體類IOSCompiler實現抽象方法,在IOS設備上編譯並運行Swift代碼。
下面的Python代碼用於實現模板方法設計模式:
from abc import ABCMeta, abstractmethod
class Compiler(metaclass=ABCMeta):
@abstractmethod
def collect_source(self):
pass
@abstractmethod
def compile_to_object(self):
pass
@abstractmethod
def run(self):
pass
def compile_and_run(self):
self.collect_source()
self.compile_to_object()
self.run()
class IOSCompiler(Compiler):
def collect_source(self):
print("收集源碼")
def compile_to_object(self):
print("編譯源碼")
def run(self):
print("運行程序")
ios = IOSCompiler()
ios.compile_and_run()
生活中的模板方法模式
想象一個旅行社的例子,它們定義了各種旅行線路,並提供度假套裝行程。一個行程套餐本質上是你作爲客戶允諾的一次旅行。旅行還涉及一些詳細信息,如遊覽的地點、交通方式和旅行有關的其他因素。當然,同樣的行程可以根據客戶的需求進行不同的定製。這種情況下,模板方法模式很適合;
設計注意事項:
1、對於上述場景,我們應該創建一個定義旅行的AbstractClass接口
2、旅行應包含多個抽象方法,定義所使用的交通方式,在第一天、第二天和第三天所遊覽的地點(假設這是一個爲期三天的旅行),並定義回程
3、模板方法itinerary()將實際定義該旅行的行程
4、我們應該定義ConcreteClasses,以幫助我們根據客戶的需要對旅行進行相應的定製
我們從抽象類開始,即Trip:
1、抽象對象由Trip類表示。他是一個接口,定義了不同日子使用的交通方式和參觀的地點等細節
2、set_transport是一個抽象方法,它由ConcreteClass實現,作用是設置交通的方式;
3、day1()、day2()、day3()抽象方法定義了特定日期所參觀的地點
4、itierary()模板方法創建完整的行程。旅行的序列爲,首先定義交通模式,然後每天參觀的地點,以及return_home
代碼如下:
from abc import ABCMeta, abstractmethod
class Trip(metaclass=ABCMeta):
@abstractmethod
def set_transport(self):
pass
@abstractmethod
def day1(self):
pass
@abstractmethod
def day2(self):
pass
@abstractmethod
def day3(self):
pass
@abstractmethod
def return_home(self):
pass
def itinerary(self):
self.set_transport()
self.day1()
self.day2()
self.day3()
self.return_home()
我們還要開發了代表具體類的某些類:
1、在本例中,我們主要有兩個實現Trip接口的具體類:VeniceTrip和MaldivesTrip;
2、這兩個具體類代表遊客根據他們的選擇和興趣所進行的兩次不同的旅行
3、VeniceTrip和MaldivesTrip都實現了set_transport()、day1()、day2()、day3()和return_home()
class VeniceTrip(Trip):
def set_transport(self):
print("乘船從大運河走")
def day1(self):
print("嘆息橋")
def day2(self):
print("聖馬可廣場")
def day3(self):
print("鳳凰歌劇院")
def return_home(self):
print("威尼斯返回")
class MaldivesTrip(Trip):
def set_transport(self):
print("飛機")
def day1(self):
print("日光浴")
def day2(self):
print("潛水")
def day3(self):
print("欣賞海洋生物")
def return_home(self):
print("馬爾代夫返回")
現在,讓我們來考察一些旅行社和希望度過一個愉快的遊客:
1、TravelAgency類代表該示例中的Client對象
2、它定義了arrange_trip()方法,讓客戶選擇歷史旅行或者海灘旅行
3、根據旅遊者的選擇,相應的類將被實例化
4、這個對象然後調用itinerary()模板方法,並根據客戶的選擇爲遊客安排相應的旅行
class TravelAgency(object):
def arrange_trip(self):
choice = input("historical or beach")
if choice == "historical":
self.trip = VeniceTrip()
self.trip.itinerary()
if choice == "beach":
self.trip = MaldivesTrip()
self.trip.itinerary()
TravelAgency().arrange_trip()
模板方法模式——鉤子
鉤子是在抽象類中聲明的方法,它通常被賦予一個默認實現。鉤子背後的思想是爲子類提供按需鉤取算法的能力。但是它並不強制子類使用鉤子,它可以很容易地忽略這一點
好萊塢原則與模板方法
好萊塢原則是一種設計原則,即不要給我們打電話,我們會打給你。它來自好萊塢哲學,如果有適合演員的角色,影棚會給演員打電話。
在面向對象的世界中,我們允許低層組件使用好萊塢原則將自己掛入系統中,然而,高層組件確定底層系統的使用方式,以及何時需要它們。換句話說,高層組件對待底層組件的方式也是不要給我們打電話,我們會打電話給你。
這涉及模板方法模式,在這個意義上,它是高級抽象類,它安排定義算法的步驟。根據算法的工作方式,通過調用底層類來定義各個步驟的具體實現。
模板方法模式的優點和缺點
優點:
1、沒有代碼重複
2、由於模板方法模式使用繼承而不是合成,因此能夠對代碼進行重用。所以,只有爲數不多的幾個方法需要重寫
3、靈活性允許子類決定如何實現算法中的步驟
缺點:
1、調試和理解模板方法模式中的流程程序序列有時會令人困惑。你最終實現的方法可能是一個不應該實現的方法,或根本沒有實現抽象的方法。文檔和嚴格的錯誤處理必須由程序員完成
2、模板框架的維護可能是一個問題,因爲任何層次的變更都可能對實現造成干擾。因此使用模板方法模式可能會使維護變的異常痛苦