編程思想從實例談面向對象編程(OOP)、工廠模式和重構_第1頁
編程思想從實例談面向對象編程(OOP)、工廠模式和重構_第2頁
編程思想從實例談面向對象編程(OOP)、工廠模式和重構_第3頁
編程思想從實例談面向對象編程(OOP)、工廠模式和重構_第4頁
編程思想從實例談面向對象編程(OOP)、工廠模式和重構_第5頁
已閱讀5頁,還剩4頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、編程思想丨從實例談面向對象編程(OOP)、工廠模式和重構程序樂園有了翅膀才能飛,欠缺靈活的代碼就象凍壞了翅膀的鳥兒。不能飛翔,就少了幾許靈動的氣韻。我們需要給代碼帶去溫暖的陽光,讓僵冷的翅膀重新飛起來。結合實例,通過應用OOP、設計模式和重構,你會看到代碼是怎樣一步一步復活的。為了更好的理解設計思想,實例盡可能簡單化。但隨著需求的增加,程序將越來越復雜。此時就有修改設計的必要,重構和設計模式就可以派上用場了。最后當設計漸趨完美后,你會發現,即使需求不斷增加,你也可以神清氣閑,不用為代碼設計而煩惱了。假定我們要設計一個媒體播放器。該媒體播放器目前只支持音頻文件mp3和wav。如果不談設計,設計岀

2、來的播放器可能很簡單:public class MediaPlayerprivate void PlayMp3()MessageBox.Show(Play the mp3 file.);private void PlayWav()MessageBox.Show(Play the wav file.);public void Play(stri ng audioType)switch (audioType.ToLower()case (mp3):PlayMp3();break;case (wav):PlayWav();break;自然, 你會發現這個設計非常的糟糕。 因為它根本沒有為未來的需求變

3、更提供最起碼的擴展。 如 果你的設計結果是這樣, 那么當你為應接不暇的需求變更而焦頭爛額的時候, 你可能更希望讓這 份設計到它應該去的地方, 就是桌面的回收站。 仔細分析這段代碼, 它其實是一種最古老的面向 結構的設計。如果你要播放的不僅僅是 mp3 和 wav, 你會不斷地增加相應地播放方法,然后讓 switch 子句越來越長,直至達到你視線看不到的地步。好吧,我們先來體驗對象的精神。根據 OOP 的思想,我們應該把 mp3 和 wav 看作是一個獨立 的對象。那么是這樣嗎?public class MP3public void Play()MessageBox.Show(Play the

4、mp3 file.);public class WAVpublic void Play()MessageBox.Show(Play the wav file.);好樣的,你已經知道怎么建立對象了。 更可喜的是, 你在不知不覺中應用了重構的方法,把原來 那個垃圾設計中的方法名字改為了統一的 Play() 方法。 你在后面的設計中, 會發現這樣改名是多 么的關鍵!但似乎你并沒有擊中要害,以現在的方式去更改 MediaPlayer 的代碼,實質并沒有多 大的變化。既然 mp3 和 wav 都屬于音頻文件,他們都具有音頻文件的共性,為什么不為它們建立一個共同 的父類呢?public class Aud

5、ioMediapublic void Play()MessageBox.Show(Play the AudioMedia file.);現在我們引入了繼承的思想, OOP 也算是象模象樣了。得意之余,還是認真分析現實世界吧。 其實在現實生活中,我們播放的只會是某種具體類型的音頻文件,因此這個 AudioMedia 類并沒 有實際使用的情況。對應在設計中,就是:這個類永遠不會被實例化。所以,還得動一下手術, 將其改為抽象類。好了,現在的代碼有點 OOP 的感覺了:public abstract class AudioMediapublic abstract void Play();public

6、class MP3:AudioMediapublic override void Play()MessageBox.Show(Play the mp3 file.);public class WAV:AudioMediapublic override void Play()MessageBox.Show(Play the wav file.);public class MediaPlayerpublic void Play(AudioMedia media)media.Play();看看現在的設計,即滿足了類之間的層次關系,同時又保證了類的最小化原則, 更利于擴展(到 這里,你會發現 play

7、 方法名改得多有必要)。即使你現在又增加了對WMA 文件的播放,只需要設計 WMA 類,并繼承 AudioMedia ,重寫 Play 方法就可以了, MediaPlayer 類對象的 Play 方 法根本不用改變。是不是到此就該畫上圓滿的句號呢?然后刁鉆的客戶是永遠不會滿足的, 他們在抱怨這個媒體播 放器了。 因為他們不想在看足球比賽的時候, 只聽到主持人的解說, 他們更渴望看到足球明星在 球場奔跑的英姿。也就是說,他們希望你的媒體播放器能夠支持視頻文件。你又該痛苦了, 因為 在更改硬件設計的同時, 原來的軟件設計結構似乎出了問題。 因為視頻文件和音頻文件有很多不 同的地方, 你可不能偷懶,

8、 讓視頻文件對象認音頻文件作父親啊。 你需要為視頻文件設計另外的 類對象了,假設我們支持 RM 和 MPEG 格式的視頻: public abstract class VideoMediapublic abstract void Play();public class RM:VideoMediapublic override void Play()MessageBox.Show(Play the rm file.);public class MPEG:VideoMediapublic override void Play()MessageBox.Show(Play the mpeg file.

9、);糟糕的是,你不能一勞永逸地享受原有的MediaPlayer 類了。因為你要播放的 RM 文件并不是AudioMedia 的子類。不過不用著急,因為接口這個利器你還沒有用上(雖然你也可以用抽象類,但在 C# 里只支持類 的單繼承)。雖然視頻和音頻格式不同,別忘了,他們都是媒體中的一種,很多時候,他們有許 多相似的功能, 比如播放。 根據接口的定義, 你完全可以將相同功能的一系列對象實現同一個接 口:public interface IMediavoid Play();public abstract class AudioMedia:IMediapublic abstract void Pla

10、y();public abstract class VideoMedia:IMediapublic abstract void Play();再更改一下 MediaPlayer 的設計就 OK 了:public class MediaPlayerpublic void Play(IMedia media)media.Play();現在可以總結一下,從 MediaPlayer 類的演變,我們可以得出這樣一個結論:在調用類對象的屬 性和方法時,盡量避免將具體類對象作為傳遞參數,而應傳遞其抽象對象,更好地是傳遞接口, 將實際的調用和具體對象完全剝離開,這樣可以提高代碼的靈活性。不過,事情并沒有完。雖

11、然一切看起來都很完美了,但我們忽略了這個事實,就是忘記了 MediaPlayer 的調用者。 還記得文章最開始的 switch 語句嗎?看起來我們已經非常漂亮地除掉了 這個煩惱。事實上,我在這里玩了一個詭計,將 switch 語句延后了。雖然在 MediaPlayer 中, 代碼顯得干凈利落,其實煩惱只不過是轉嫁到了 MediaPlayer 的調用者那里。例如,在主程序界面中:Public void Bt nPlay_Click(object sen der,Eve ntArgs e)switch (cbbMediaType.Selectltem.ToStri ng().ToLower()IM

12、edia media;case (mp3):media = new MP3();break;case (wav):media = new WAV();break;/其它類型略;MediaPlayer player = new MediaPlayer();player.Play(media);用戶通過選擇 cbbMediaType組合框的選項,決定播放哪一種文件,然后單擊Play按鈕執行。現在該設計模式粉墨登場了,這種根據不同情況創建不同類型的方式,工廠模式是最拿手的。看看我們的工廠需要生產哪些產品呢?雖然這里有兩種不同類型的媒體AudioMedia和VideoMedia (以后可能更多),但它

13、們同時又都實現IMedia接口,所以我們可以將其視為一種產品,用工廠方法模式就可以了。首先是工廠接口:public in terface IMediaFactoryIMedia CreateMedia();然后為每種媒體文件對象搭建一個工廠,并統一實現工廠接口:public class MP3MediaFactory:IMediaFactorypublic IMedia CreateMedia()return new MP3();public class RMMediaFactory:IMediaFactorypublic IMedia CreateMedia()return new RM()

14、;/其它工廠略;寫到這里,也許有人會問,為什么不直接給AudioMedia和VideoMedia類搭建工廠呢?很簡單,因為在AudioMedia和VideoMedia中,分別還有不同的類型派生,如果為它們搭建工廠,則在 CreateMedia()方法中,仍然要使用Switch語句。而且既然這兩個類都實現了IMedia接口,可以認為是一種類型,為什么還要那么麻煩去請動抽象工廠模式,來生成兩類產品呢?可能還會有人問,即使你使用這種方式,那么在判斷具體創建哪個工廠的時候,不是也要用到switch語句嗎?我承認這種看法是對的。不過使用工廠模式,其直接好處并非是要解決switch語句的難題,而是要延遲對

15、象的生成,以保證的代碼的靈活性。當然,我還有最后一招殺手锏沒有使岀來,到后面你會發現,switch語句其實會完全消失。還有一個問題,就是真的有必要實現AudioMedia和VideoMedia兩個抽象類嗎?讓其子類直接實現接口不更簡單?對于本文提到的需求,我想你是對的, 但不排除AudioMedia和VideoMedia它們還會存在區別。例如音頻文件只需要提供給聲卡的接口,而視頻文件還需要提供給顯卡的接口。如果讓 MP3、WAV、RM、MPEG 直接實現 IMedia 接口,而不通過 AudioMedia 和 VideoMedia, 在滿足其它需求的設計上也是不合理的。當然這已經不包括在本文的

16、范疇了。現在主程序界面發生了稍許的改變:Public void BtnPlay_Click(object sender,EventArgs e)IMediaFactory factory = n ull;switch (cbbMediaType.Selectltem.ToStri ng().ToLower()case (mp3):factory = new MP3MediaFactory();break;case (wav):factory = new WAVMediaFactory();break;/其他類型略;MediaPlayer player = new MediaPlayer();p

17、layer.Play(factory.CreateMedia();寫到這里,我們再回過頭來看MediaPlayer類。這個類中,實現了 Play方法,并根據傳遞的參數,調用相應媒體文件的Play方法。在沒有工廠對象的時候,看起來這個類對象運行得很好。如果是作為一個類庫或組件設計者來看,他提供了這樣一個接口,供主界面程序員調用。然而在引入工廠模式后,在里面使用MediaPlayer類已經多余了。所以,我們要記住的是,重構并不僅僅是往原來的代碼添加新的內容。當我們發現一些不必要的設計時,還需要果斷地刪掉這些冗余代碼。Public void BtnPlay_Click(object sender,E

18、ventArgs e)IMediaFactory factory = n ull;switch (cbbMediaType.Selectltem.ToStr in g().ToLower()case (” mp3):factory = new MP3MediaFactory();break;case ( wav ):factory = new WAVMediaFactory();break;/其他類型略;IMedia media = factory.CreateMedia();media.Play();如果你在最開始沒有體會到IMedia接口的好處,在這里你應該已經明白了。我們在工廠中用到了該

19、接口;而在主程序中,仍然要使用該接口。使用接口有什么好處?那就是你的主程序可以在沒有具體業務類的時候,同樣可以編譯通過。 因此,即使你增加了新的業務,你的主程序是不用 改動的。不過,現在看起來,這個不用改動主程序的理想,依然沒有完成。看到了嗎?在Btn Play_Click()中,依然用new創建了一些具體類的實例。如果沒有完全和具體類分開,一旦更改了具體類的 業務,例如增加了新的工廠類,仍然需要改變主程序,何況討厭的switch語句仍然存在,它好像是翅膀上滋生的毒瘤,提示我們,雖然翅膀已經從僵冷的世界里復活,但這雙翅膀還是有病的,并不能正常地飛翔。是使用配置文件的時候了。 我們可以把每種媒體

20、文件類類型的相應信息放在配置文件中, 然后根 據配置文件來選擇創建具體的對象。并且, 這種創建對象的方法將使用反射來完成。 首先, 創建 配置文件:然后,在主程序界面的 Form_Load 事件中,讀取配置文件的所有 key 值,填充 cbbMediaType 組合框控件:public void Form_Load(object sender, EventArgs e) cbbMediaType.Items.Clear();foreach (string key in ConfigurationSettings.AppSettings.AllKeys) cbbMediaType.Item.Add(key); cbbMediaType.SelectedIndex = 0;最后,更改主程序的 Play 按鈕單擊事件:Public

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論