一、股票查詢客戶端
投資股票是種常見的理財方式,我國股民越來越多,實時查詢股票的需求也越來越大。今天,我們通過一個簡單的股票查詢客戶端來認識一種簡單的設計模式:模板模式。
根據股票代碼來查詢股價分為如下幾個步驟:登錄、設置股票代碼、查詢、展示。構造如下的虛擬股票查詢器:
class StockQueryDevice():stock_code="0"stock_price=0.0def login(self,usr,pwd):passdef setCode(self,code):self.stock_code=codedef queryPrice(self):passdef 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 Trueelse:print "Web A:Login ERROR... user:%s pwd:%s"%(usr,pwd)return Falsedef queryPrice(self):print "Web A Querying...code:%s "%self.stock_codeself.stock_price=20.00def 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 Trueelse:print "Web B:Login ERROR... user:%s pwd:%s"%(usr,pwd)return Falsedef queryPrice(self):print "Web B Querying...code:%s "%self.stock_codeself.stock_price=30.00def 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.0def login(self,usr,pwd):passdef setCode(self,code):self.stock_code=codedef queryPrice(self):passdef showPrice(self):passdef 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 Falseself.setCode(code)self.queryPrice()self.showPrice()return True
在模板模式中,像這樣類似于login等根據特定情況,定制某些特定動作的函數,被稱作鉤子函數。此例中,如果登錄失敗(user:myStock B,pwd:myPwdA),會打印如下結果:
Web A:Login ERROR... user:myStockB pwd:myPwdA
二、模板模式
模板模式定義如下:定義一個操作中的算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可重新定義該算法的某些特定的步驟。子類實現的具體方法叫作基本方法,實現對基本方法高度的框架方法,叫作模板方法。
三、模板模式的優點和應用
優點:
1、可變的部分可以充分擴展,不變的步驟可以充分封裝;
2、提取公共代碼,減少冗余代碼,便于維護;
3、具體過程可以定制,總體流程方便掌控。
使用場景:
1、某超類的子類中有公有的方法,并且邏輯基本相同,可以使用模板模式。必要時可以使用鉤子方法約束其行為。具體如本節例子;
2、比較復雜的算法,可以把核心算法提取出來,周邊功能在子類中實現。例如,機器學習中的監督學習算法有很多,如決策樹、KNN、SVM等,但機器學習的流程大致相同,都包含輸入樣本、擬合(fit)、預測等過程,這樣就可以把這些過程提取出來,構造模板方法,并通過鉤子方法控制流程。
四、模板模式的缺點
1、模板模式在抽象類中定義了子類的方法,即子類對父類產生了影響,部分影響了代碼的可讀性。