1. 程式人生 > >16-Python與設計模式--模板模式

16-Python與設計模式--模板模式

一、股票查詢客戶端

投資股票是種常見的理財方式,我國股民越來越多,實時查詢股票的需求也越來越大。今天,我們通過一個簡單的股票查詢客戶端來認識一種簡單的設計模式:模板模式。
根據股票程式碼來查詢股價分為如下幾個步驟:登入、設定股票程式碼、查詢、展示。構造如下的虛擬股票查詢器:

複製程式碼
class StockQueryDevice():
    stock_code="0"
    stock_price=0.0
    def login(self,usr,pwd):
        pass
    def setCode(self,code):
        self.stock_code=code
    def queryPrice(self):
        pass
    def showPrice(self):
        pass
複製程式碼

 

現在查詢機構很多,我們可以根據不同的查詢機構和查詢方式,來通過繼承的方式實現其對應的股票查詢器類。例如,WebA和WebB的查詢器類可以構造如下:

複製程式碼
class WebAStockQueryDevice(StockQueryDevice):
    def login(self,usr,pwd):
        if usr=="myStockA" and pwd=="myPwdA":
            print "Web A:Login OK... user:%s pwd:%s"%(usr,pwd)
            return True
        else:
            print "Web A:Login ERROR... user:%s pwd:%s"%(usr,pwd)
            return False
    def queryPrice(self):
        print "Web A Querying...code:%s "%self.stock_code
        self.stock_price=20.00
    def showPrice(self):
        print "Web A Stock Price...code:%s price:%s"%(self.stock_code,self.stock_price)
class WebBStockQueryDevice(StockQueryDevice):
    def login(self,usr,pwd):
        if usr=="myStockB" and pwd=="myPwdB":
            print "Web B:Login OK... user:%s pwd:%s"%(usr,pwd)
            return True
        else:
            print "Web B:Login ERROR... user:%s pwd:%s"%(usr,pwd)
            return False
    def queryPrice(self):
        print "Web B Querying...code:%s "%self.stock_code
        self.stock_price=30.00
    def showPrice(self):
        print "Web B Stock Price...code:%s price:%s"%(self.stock_code,self.stock_price)
複製程式碼

 

在場景中,想要在網站A上查詢股票,需要進行如下操作:

if  __name__=="__main__":
    web_a_query_dev=WebAStockQueryDevice()
    web_a_query_dev.login("myStockA","myPwdA")
    web_a_query_dev.setCode("12345")
    web_a_query_dev.queryPrice()
    web_a_query_dev.showPrice()

列印結果如下:


Web A:Login OK... user:myStockA pwd:myPwdA
Web A Querying...code:12345 
Web A Stock Price...code:12345 price:20.0


每次操作,都會呼叫登入,設定程式碼,查詢,展示這幾步,是不是有些繁瑣?既然有些繁瑣,何不將這幾步過程封裝成一個介面。由於各個子類中的操作過程基本滿足這個流程,所以這個方法可以寫在父類中:

複製程式碼
class StockQueryDevice():
    stock_code="0"
    stock_price=0.0
    def login(self,usr,pwd):
        pass
    def setCode(self,code):
        self.stock_code=code
    def queryPrice(self):
        pass
    def showPrice(self):
        pass
    def operateQuery(self,usr,pwd,code):
        self.login(usr,pwd)
        self.setCode(code)
        self.queryPrice()
        self.showPrice()
        return True
複製程式碼
 

這樣,在業務場景中,就可以通過operateQuery一氣呵成了。

if  __name__=="__main__":
    web_a_query_dev=WebAStockQueryDevice()
    web_a_query_dev.operateQuery("myStockA","myPwdA","12345")

 

這種基本每個程式設計師都會想到的解決方案,就是模板模式。很簡單吧。
但也許你會問,登入並不一定每次都會成功呀?是的,所以在operateQuery介面中需要做一重判斷,寫成:

複製程式碼
def operateQuery(self,usr,pwd,code):
    if not self.login(usr,pwd):
        return False
    self.setCode(code)
    self.queryPrice()
    self.showPrice()
    return True
複製程式碼

 

在模板模式中,像這樣類似於login等根據特定情況,定製某些特定動作的函式,被稱作鉤子函式。此例中,如果登入失敗(user:myStock B,pwd:myPwdA),會列印如下結果:
Web A:Login ERROR... user:myStockB pwd:myPwdA

 

二、模板模式

模板模式定義如下:定義一個操作中的演算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變一個演算法的結構即可重新定義該演算法的某些特定的步驟。子類實現的具體方法叫作基本方法,實現對基本方法高度的框架方法,叫作模板方法。


f1.png

 

三、模板模式的優點和應用

優點:

1、可變的部分可以充分擴充套件,不變的步驟可以充分封裝;
2、提取公共程式碼,減少冗餘程式碼,便於維護;
3、具體過程可以定製,總體流程方便掌控。

使用場景:

1、某超類的子類中有公有的方法,並且邏輯基本相同,可以使用模板模式。必要時可以使用鉤子方法約束其行為。具體如本節例子;
2、比較複雜的演算法,可以把核心演算法提取出來,周邊功能在子類中實現。例如,機器學習中的監督學習演算法有很多,如決策樹、KNN、SVM等,但機器學習的流程大致相同,都包含輸入樣本、擬合(fit)、預測等過程,這樣就可以把這些過程提取出來,構造模板方法,並通過鉤子方法控制流程。

四、模板模式的缺點

1、模板模式在抽象類中定義了子類的方法,即子類對父類產生了影響,部分影響了程式碼的可讀性。