設計模式初學者系列-策略模式 -------為什么總是繼承

設計模式初學者系列-策略模式

??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? -------為什么總是繼承

模板方法的延續

這篇稿子是基于我的前一篇模板方法設計模式之上演繹的,如果沒有閱讀請點擊這里查看,以了解這篇稿子的上下文。

在模板方法設計模式里我舉了一個例子:教育部規定了新生報到流程的算法骨架,然后這個算法骨架中的一些關鍵步驟由各高校自由的去發揮。我在這個例子中將高校設為一個抽象類,各高校要實現的算法步驟都是抽象方法。我還給出了兩個高校的實現代碼:清華大學和北京大學。在這個例子中本沒有什么問題,但是軟件總是會變的。

當有更多的高校要實現的時候,我們就會發現,很多高校的有些報到步驟實現是一樣的,這就存在子類中有大量的重復代碼,重復總是會出問題的。當然我們可以使用Martin Fowler的Pull Up Method(Refactoring P320)重構方法,將這些共同的部分推移到高校這個父類實現,并將這個抽象類改為virtual。

clip_image001

public abstract class 高校
{

public void 報到()

{

教務處報到();

繳費();

本院系報到();

教材科發教材();

}

protected abstract void 教務處報到();

//方法由抽象的更改為虛方法

protected virtual void 繳費()

{

//將這個方法在父類去實現,因為好多高校的實現都是這樣的,避免重復

}

protected abstract 專業等信息 本院系報到();

protected abstract 教材 教材科發教材();

}

但是,現在出現了這樣的情況:A,B,C等幾個大學的實現在某些步驟上有些相同,D,F在某些步驟的實現有些相同,也許你會說:這不好辦,繼續使用繼承唄,將共同的東西往上推,并且在“高校類”和各高校實現的類中間插入一些類,這些類將提供共同的實現。好像是個很好的辦法。來瞧一瞧:

clip_image003

重復的代碼確實減少了很多,但是還有一些重復(心里在默默的罵道:TMD,為什么C#不支持多繼承,不然我就可以消除重復了),也許你還在自我陶醉的欣賞著自己多么完美的類繼承層次,在那里感慨OO的強大。但是隨著具體的高校越來越多,而且有的高校的報到步驟居然要發生改變,你小心的在中間那一層添加新的類,并將一些高校的實現轉移,每一次你都非常小心(這個系統正在高速的運轉,每改錯一步,就有多少莘莘學子入不了學)。你心里終于對OO不滿起來:為什么,為什么大家都說OO是救世主,但是卻救不了我。答案是因為你將OO的設計原則遺忘在課本里了。開閉原則、優先使用組合,你還記得嗎?

在我們很多OO程序員的腦子里總是存在這樣一個觀念:沒有繼承的程序不是OO的程序,看到重復總是想到繼承。當初我也是這樣想的,有的時候看到自己畫的龐大的繼承類圖,心里在樂呵呵的笑。可繼承總是不給面子,一個小小的變化就將這個看似穩定的體系弄的支離破碎。

還是回到我們的例子,在這個例子中變化的是各高校的報到步驟,本著發現變化、封裝變化、隔離變化的原則我們將報到的步驟分離出來,獨立成類。

clip_image005

這樣我們就可以復用這些步驟了,有新的步驟實現只要添加更多的子類,并不需要修改原來的代碼。(作業:在繼續閱讀之前根據上面的類圖自己寫出實現的代碼來)

這就是所謂的策略設計模式:策略模式定義了算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨立于算法的客戶(DP)。

策略模式有三種參與者:

一、 Context 這個類保存了對策略的引用,并且調用實際的策略實現,有可能還提供一個接口,讓策略可以訪問它內部的數據,在這里就是我們的“高校”類。

二、 Strategy 策略接口,給算法族定義一個通用的接口,讓客戶以一種一致的方法去訪問。(I教務處報到,I繳費)

三、 ConcreteStrategy 這就是具體的策略實現了,實現策略接口(各報到步驟的實現)。

如下圖:

clip_image006

在我們的例子中報到的步驟就是算法族 比如“繳費”這個步驟,有多種繳費方式,我們將其封裝起來,客戶調用的時候并不需要了解你是怎么實現這個“繳費”的,這個過程對于客戶來說是透明的。這些不同的“繳費”步驟之間是可以無縫的替換,而客戶對此一點都不知覺。

好了,既然解決方案提出來了,我們就來實現它吧

首先我們定義所有的報到步驟的接口:

public interface I教務處報到

{

void 教務處報到();

}

public interface I繳費

{

void 繳費();

}

public interface I本院系報到

{

void 本院系報到();

}

public interface I教材科發教材

{

void 教材科發教材();

}

下面我實現兩個教務處報到的步驟,其他的就當作課后作業了,呵呵。

public class 教務處報到A : I教務處報到

{

public void 教務處報到()

{

Console.Write("教務處報到,A類實現");

}

}

public class 教務處報到B : I教務處報到

{

public void 教務處報到()

{

MessageBox.Show("教務處報到,B類實現");

}

}

再看看我們的高校類的實現吧:

public class 高校

{

public 高校(I教務處報到 p教務處報到,I繳費 p繳費,I本院系報到 p本院系報到,I教材科發教材 p教材科發教材)

{

this._教務處報到 = p教務處報到;

this._繳費 = p繳費;

this._本院系報到 = p本院系報到;

this._教材科發教材 = p教材科發教材;

}

//為什么有了賦值的構造函數還要暴露這么多只寫屬性出來呢?

//這樣就可以在運行時改變高校的報到步驟了,

//假如報到系統出現故障我們可以馬上采取另外一種方案

//而不需要停止系統的運行

I教務處報到 _教務處報到;

public I教務處報到 教務處報到

{

set

{

_教務處報到 = value;

}

}

I繳費 _繳費;

public I繳費 繳費

{

set

{

_繳費 = value;

}

}

I本院系報到 _本院系報到;

public I本院系報到 本院系報到

{

set

{

_本院系報到 = value;

}

}

I教材科發教材 _教材科發教材;

public I教材科發教材 教材科發教材

{

set

{

_教材科發教材 = value;

}

}

//用上了策略模式,模板方法更加靈活了

//但現在還是不是模板方法了?

public void 報到()

{

教務處報到.教務處報到();

繳費.繳費();

本院系報到.本院系報到();

教材科發教材.教材科發教材();

}

}

Ok,我就把代碼寫這么多了,要這個代碼運行起來還需要一些補充,這個高校類如何進行實例化才能更靈活也值得考慮。

看到沒,利用組合我們也可以達到代碼復用的目的,而且沒有繼承的弊端。

上面好像都是在說策略模式的好話,那策略模式有沒有副作用呢?當然有

一、 雖說客戶代碼無須關心各個策略是如何實現的,但是它們還是要知道有多少種策略實現,該實現是干什么的,也就是客戶代碼需要知道策略的一些細節,這樣才可以根據需要使用哪個策略,但是我們可以使用創建型模式來解決這個問題。

二、 有的時候策略需要從Context那里獲取一些數據,這樣造成雙向的關聯,而且有可能幾個策略需要的數據都不一樣,但是為了一致性不得不向它們傳遞相同的數據。

三、 也許大家會發現,使用策略模式后出現很多小類,實際上這也是所有設計模式的“通病”。

現實中的策略模式

大家對于PetShop這個應用肯定很熟悉,在PetShop 4.0里面就使用了策略設計模式:

在Petshop4的BLL項目中有一個OrderAsynchronous類和一個OrderSynchronous類,它們都繼承自IorderStrategy。OrderSynchronous以一種同步的方式處理訂單,而OrderAsynchronous先將訂單放在隊列里,然后再對隊列里的訂單進行處理,以一種異步方式。而在BLL中的Order類里通過反射從配置文件讀取策略配置的信息以決定到底是使用哪種訂單處理方式。這里就不貼代碼了,有興趣的可以去下載PetShop看看,主要關注這幾個:PetShop.BLL.Order(如何使用策略以及如何根據配置文件實例化具體的策略)、PetShop.IBLLStrategy. IorderStrategy(策略的接口)、PetShop.BLL. OrderSynchronous、PetShop.BLL. OrderAsynchronous。

總結

在本篇我們從模板方法談起,聊了一些模板方法隨著項目的發展可能造成的問題,但這并不是模板方法的弊端,模板方法關注的是算法骨架的復用,如果你發覺新的問題出現,這可能就是模板方法不再適用的信號。通過我們對項目的擴展,發現繼承在某些時候并不是都能達到代碼復用的目的,這個時候我們應該考慮組合了,而且繼承是一種靜態的編譯期的行為(針對像C#這種強類型靜態語言而言),代碼一經寫定我們就沒有選擇的余地了。

前幾天和別人在群里閑聊,談到怎樣學習設計模式,有人說設計模式靠悟,有人說設計模式靠經驗的積累。悟也好,經驗積累也好,我的感覺是不要把設計模式當作圣經,當一個人把一個事物當作圣經的時候總是很珍惜她,而且不會去褻瀆她,這是學習模式的障礙。對于初學者來說應該有“熟讀唐詩三百首,不會吟詩也會吟”的決心。

轉載于:https://www.cnblogs.com/yuyijq/archive/2008/01/14/1038286.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276965.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276965.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276965.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

紅米airdots掉了怎么查找_紅米K30 Pro 榮耀V30pro 這兩款手機該怎么選呢?

點擊?玩機數碼君?關注我,加★星標★你好 我是歲月神偷昨天可以說是小米拍手稱快的一天,紅米K30 Pro以2999的超低價成為目前最便宜的驍龍865旗艦,讓友商拍馬難追。友商明眼人都知道說的華為,怎么感覺小米每次發布會也替華為宣傳了…

返回一個循環整數組最大子數組和

任務要求: 1、輸入一個整形數組,數組里有正數也有負數。 2、數組中連續的一個或多個整數組成一個子數組,每個子數組都有一個和。 3、如果數組A[0]……A[j-1]首尾相鄰,允許A[i-1], …… A[n-1], A[0]……A…

中文分詞入門之字標注法4

http://www.52nlp.cn/%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D%E5%85%A5%E9%97%A8%E4%B9%8B%E5%AD%97%E6%A0%87%E6%B3%A8%E6%B3%954 上一節主要介紹的是利用最大熵工具包來做字標注中文分詞,這一節我們直奔主題,借用條件隨機場工具“CRF: Yet Another CRF …

后臺設置 datakeynames

FormView2.DataKeyNames new String[] { "ShoppingRemarkID" }; 轉載于:https://www.cnblogs.com/Fernando/archive/2008/01/16/1041717.html

4K 海思 聯詠 芯片_老電視也有春天,換裝海美迪4K電視盒子H7 Plus

寫在前面YALL,大家好,我是老炮兒許老板。疫情進入六月逐漸好轉,各級各類學校相繼開學,年前給兒子報的托班也終于迎來了開學,平日里幫忙照看兒子的爺爺奶奶也終于得到了解放。現在白天有大把大把的時間來追劇看電視&…

知識付費不熱了,得到們接下來故事怎么講?

如今,知識付費風口已過,紅利期隨之在逐漸消失,很多知識付費的創業者也在感嘆生意難做,甚至有人已經開始離場。比如,百度音頻知識付費產品只生存了6個月,最后的更新時間則停在7月13日。再如,在分…

I'm genius,用游戲柄控制鼠標

昨天不小心把鼠標放在店里沒有帶回來,今天電腦就沒有鼠標了,用了很久的鍵盤,實在不爽,就突然發現自己有一個游戲柄,后來到網上查了一下怎么用游戲柄控制鼠標,呵呵,發現了一個,下載用…

WCF從理論到實踐(4):路在何方

本文的出發點 通過閱讀本文,您能了解以下知識: Address是什么? Address的組成? 如何在配置文件中指定Address? 如何通過編程方式設置Address? Address有什么特殊應用? 本文適合的讀者 適合WCF初學者&#xff…

office 2007圖標_微軟Office 365桌面版新圖標開始測試

IT之家3月1日消息 此前,微軟公布了全新的Office圖標,微軟Office 365在線網頁版在2月15日開始已經全面更新新版圖標,而桌面版Office 365現在也陸續開始測試新版圖標。目前微軟Office Dogfood通道上推送的開發者預覽版本已經在2月27日開始測試O…

【動態規劃BFS】相遇

這是我第一次模擬題測試點全部AC。。。 同機房的DALAO都用的BFS 然而我用的DP(其實不會BFS) 話不多說,上題! (灰常詳細)DP解法: 重點還是狀態轉移方程式的推導 1個點i要么是后面的位置i-1往前走…

Ruby on Rails 通過代理遠程安裝

在網上查了一些資料,都不詳細,現在列出標準命令: 1。如果代理服務器需要認證 gem install rails --include-dependencies --http-proxy http://username:passwordproxy:port 2。如果代理服務器不需要認證 gem install rails --include-depend…

五個思路,教你如何建立金融業的數據分析管理模型

說起銀行、保險、股票投資這樣的金融行業,很多人都認為它們是依靠數據驅動的企業,畢竟大數據的誕生本來就是為了金融信息流通而服務的,但在我身邊很多搞證券、投資的朋友看來,事實卻并非如此。 真正在金融行業做數據分析的人&…

【SSH網上商城項目實戰19】訂單信息的級聯入庫以及頁面的緩存問題

購物車這一塊還剩最后兩個問題,就是訂單信息的級聯入庫和頁面緩存,這里的信息是指購物車和購物項,即我們將購物車的信息存入數據庫的同時,也存入每個購物項的信息,而且外鍵都關聯好,這涉及到了Hibernate中的…

exfat分配單元大小選多少_安防監控攝像機視角大小和鏡頭毫米數的基礎知識!...

關于選擇監控鏡頭毫米數的問題,雖然只有新手才有此困惑,但是我們還是要認真地說一說。監控視角,就是指監控照射的鏡頭所能覆蓋到的范圍,就是監控畫面所能看到的角度統稱叫監控視角。我們正常選購監控的時候,除了可以選…

彩信編輯器之預覽功能

html代碼 <table width"200"height"250"border"0"cellpadding"0"cellspacing"0"bgcolor"#666666"><tr><td align"center"valign"middle"><marquee id"MMScreen&qu…

java 幾個實用的小工具

1、除法運算 編程的人都知道&#xff0c;java中的“/”、“%”運算&#xff0c;其中前者為取整&#xff0c;后者取余數。那么有沒有快捷的運算方法取正常的運算結果呢&#xff1f; 查了資料&#xff0c;發現很簡單。代碼如下&#xff1a; public static String txfloat(int a,i…

處理模板頁菜單高亮

//處理模板頁菜單高亮var urlstatus false;$("#indexMenu a").each(function () {if ((location.href /).indexOf($(this).attr(href)) > -1 && $(this).attr(href) ! ) {$(this).parent().addClass(active);urlstatus true;} else {$(this).parent().…

動畫演示 Delphi 2007 IDE 功能[3] - 修改屬性

動畫劇本:添加控件后用 F11 激活 Object Inspector 窗口;可用 ↑ ↓ 選擇屬性;用 Tab 切換屬性名和屬性值;用 Tab 切換到屬性名后, 鍵入屬性名的部分字母, 可迅速定位;用 Tab 切換到屬性值后, 也可以鍵入字母選擇, 而后回車確認.Ctrl↓ 可以選擇其他控件;整個過程可以做到無鼠標…

kali怎么成為管理員_網站死鏈是什么、是怎么引起的以及死鏈對SEO優化的影響?...

網站死鏈是我們在做SEO時必不可少的一個錯誤&#xff0c;對于從事SEO行業的人員來說&#xff0c;網站死鏈最熟悉不過了&#xff0c;但是對于那些剛入SEO行業的新手來說&#xff0c;還是不太熟悉。今天我們就給大家講一下什么是網站死鏈&#xff1f;網站死鏈是怎么引起的&#x…

Map-Reduce入門

1、Map-Reduce的邏輯過程 假設我們需要處理一批有關天氣的數據&#xff0c;其格式如下&#xff1a; 按照ASCII碼存儲&#xff0c;每行一條記錄每一行字符從0開始計數&#xff0c;第15個到第18個字符為年第25個到第29個字符為溫度&#xff0c;其中第25位是符號/-006701199099999…