Calvin's Tech Space

          成于堅忍,毀于浮躁

             :: 首頁 :: 聯系 :: 聚合  :: 管理
           

          有了翅膀才能,欠缺靈活的代就象壞了翅膀的兒。不能翔,就少了幾的氣韻。我需要碼帶去溫暖的陽光,僵冷的翅膀重新起來。例,通過應OOP設計模式和重構,你會看到代是怎一步一步復活的。

          了更好的理解設計思想,例盡可能簡單化。但隨著需求的增加,程序將越來越復。此就有修改設計的必要,重構和設計模式就可以派上用了。最后當設計漸趨完美后,你會發現,即使需求不斷增加,你也可以神清氣,不用碼設計煩惱了。

          假定我設計一個媒體播放器。媒體播放器目前只支持音文件mp3wav。如果不談設計設計出來的播放器可能很簡單

          public class MediaPlayer {

          private void PlayMp3() {

          MessageBox.Show("Play the mp3 file.");

          }

          private void PlayWav() {

          MessageBox.Show("Play the wav file.");

          }

          public void Play(string audioType) {

          switch (audioType.ToLower()) {

          case ("mp3"):

          PlayMp3();

          break;

          case ("wav"):

          PlayWav();

          break;

          }

          }

          }

          自然,你會發現這設計非常的糟糕。因它根本沒有未來的需求更提供最起展。如果你的設計結果是這樣,那么當你為應接不暇的需求更而焦頭爛額候,你可能更希望讓這設計到它應該去的地方,就是桌面的回收站。仔分析段代,它其是一種最古老的面向設計。如果你要播放的不僅僅mp3wav,你會不斷地增加相地播放方法,然后switch子句越來越,直至達到你視線看不到的地步。

          好吧,我先來體驗對象的精神。根據OOP的思想,我們應該mp3wav看作是一個獨立的象。那么是這樣嗎

          public class MP3 {

          public void Play() {

          MessageBox.Show("Play the mp3 file.");

          }

          }

          public class WAV {

          public void Play() {

          MessageBox.Show("Play the wav file.");

          }

          }

          的,你已知道怎么建立象了。更可喜的是,你在不知不用了重構的方法,把原來那個垃圾設計中的方法名字改一的Play()方法。你在后面的設計中,會發現這樣改名是多么的關!但似乎你并沒有中要害,以在的方式去更改MediaPlayer的代實質并沒有多大的化。

          既然mp3wav都屬于音文件,他都具有音文件的共性,什么不建立一個共同的呢?

          public class AudioMedia {

          public void Play() {

          MessageBox.Show("Play the AudioMedia file.");

          }

          }

          在我引入了承的思想,OOP也算是象模象了。得意之余,真分析現實世界吧。現實生活中,我播放的只會是某種具體型的音文件,因此AudioMedia并沒有實際使用的情況。對應設計中,就是:不會被例化。所以,一下手,將其改抽象。好了,在的代有點OOP的感了:

          public abstract class AudioMedia {

          public abstract void Play();

          }

          public class MP3:AudioMedia {

          public override void Play() {

          MessageBox.Show("Play the mp3 file.");

          }

          }

          public class WAV:AudioMedia {

          public override void Play() {

          MessageBox.Show("Play the wav file.");

          }

          }

          public class MediaPlayer {

          public void Play(AudioMedia media) {

          media.Play();

          }

          }

          看看在的設計,即滿足了次關系,同又保的最小化原,更利于展(到里,你會發現play方法名改得多有必要)。即使你在又增加了WMA文件的播放,只需要設計WMA,并AudioMedia,重寫Play方法就可以了,MediaPlayer類對象的Play方法根本不用改

          是不是到此就畫上圓滿的句號呢?然后刁的客是永不會滿足的,他在抱怨個媒體播放器了。因不想在看足球比候,只聽到主持人的解,他更渴望看到足球明星在球奔跑的英姿。也就是,他希望你的媒體播放器能支持視頻文件。你又痛苦了,因在更改硬件設計的同,原來的設計結構似乎出了問題。因為視頻文件和音文件有很多不同的地方,你可不能偷懶讓視頻文件文件作父啊。你需要為視頻文件設計另外的類對象了,假支持RMMPEG格式的視頻

          public abstract class VideoMedia {

          public abstract void Play();

          }

          public class RM:VideoMedia {

          public override void Play() {

          MessageBox.Show("Play the rm file.");

          }

          }

          public class MPEG:VideoMedia {

          public override void Play() {

          MessageBox.Show("Play the mpeg file.");

          }

          }

          糟糕的是,你不能一永逸地享受原有的MediaPlayer了。因你要播放的RM文件并不是AudioMedia的子

          不用著急,因接口個利器你沒有用上(然你也可以用抽象,但在C#里只支持單繼承)。視頻和音格式不同,忘了,他都是媒體中的一種,很多候,他多相似的功能,比如播放。根據接口的定,你完全可以將相同功能的一系列實現同一個接口:

          public interface IMedia {

          void Play();

          }

          public abstract class AudioMedia:IMedia {

          public abstract void Play();

          }

          public abstract class VideoMedia:IMedia {

          public abstract void Play();

          }

          再更改一下MediaPlayer設計OK了:

          public class MediaPlayer {

          public void Play(IMedia media) {

          media.Play();

          }

          }

          在可以總結一下,從MediaPlayer的演,我可以得出這樣一個結論調類對象的屬性和方法,盡量避免將具體類對象作為傳遞參數,而應傳遞其抽象象,更好地是傳遞接口,將實際調用和具體象完全剝離開,這樣可以提高代的靈活性。

          ,事情并沒有完。然一切看起來都很完美了,但我忽略了個事,就是忘MediaPlayer調用者。還記得文章最開始的switch?看起來我非常漂亮地除掉了煩惱。事上,我在里玩了一個詭計switch句延后了。然在MediaPlayer中,代碼顯得干利落,其實煩惱只不嫁到了MediaPlayer調用者那里。例如,在主程序界面中:

          Public void BtnPlay_Click(object sender,EventArgs e) {

          switch (cbbMediaType.SelectItem.ToString().ToLower()) {

          IMedia media;

          case ("mp3"):

          media = new MP3();

          break;

          case ("wav"):

          media = new WAV();

          break;

          //其它型略;

          }

          MediaPlayer player = new MediaPlayer();

          player.Play(media);

          }

          過選擇cbbMediaType合框的選項,決定播放哪一種文件,然后單擊Play鈕執行。

          該設計模式粉墨登了,種根據不同情況建不同型的方式,工廠模式是最拿手的。先看看我的工廠需要生哪些品呢?里有兩種不同型的媒體AudioMediaVideoMedia(以后可能更多),但它又都實現IMedia接口,所以我可以將其視為一種品,用工廠方法模式就可以了。首先是工廠接口:

          public interface IMediaFactory {

          IMedia CreateMedia();

          }

          然后每種媒體文件象搭建一個工廠,并實現工廠接口

          public class MP3MediaFactory:IMediaFactory {

          public IMedia CreateMedia() {

          return new MP3();

          }

          }

          public class RMMediaFactory:IMediaFactory {

          public IMedia CreateMedia() {

          return new RM();

          }

          }

          //其它工廠略;

          寫到里,也有人會什么不直接AudioMediaVideoMedia搭建工廠呢?很簡單,因AudioMediaVideoMedia中,分別還有不同的型派生,如果搭建工廠,CreateMedia()方法中,仍然要使用Switch句。而且既然兩個實現IMedia接口,可以認為是一種型,什么要那么麻請動抽象工廠模式,來生成兩類產品呢?

          可能會有人,即使你使用種方式,那么在判斷具體建哪個工廠的候,不是也要用到switch?我承認這種看法是的。使用工廠模式,其直接好并非是要解決switch句的難題,而是要延遲對象的生成,以保的代的靈活性。當然,我有最后一招沒有使出來,到后面你會發現switch句其會完全消失。

          有一個問題,就是真的有必要實現AudioMediaVideoMedia兩個抽象類嗎其子直接實現接口不更簡單于本文提到的需求,我想你是的,但不排除AudioMediaVideoMedia們還會存在區。例如音文件只需要提供聲卡的接口,而視頻文件需要提供給顯卡的接口。如果MP3WAVRMMPEG直接實現IMedia接口,而不通AudioMediaVideoMedia,在滿足其它需求的設計上也是不合理的。當然不包括在本文的范疇了。

          在主程序界面生了稍的改

          Public void BtnPlay_Click(object sender,EventArgs e) {

          IMediaFactory factory = null;

          switch (cbbMediaType.SelectItem.ToString().ToLower()) {

          case ("mp3"):

          factory = new MP3MediaFactory();

          break;

          case ("wav"):

          factory = new WAVMediaFactory();

          break;

          //其他型略;

          }

          MediaPlayer player = new MediaPlayer();

          player.Play(factory.CreateMedia());

          }

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

          Public void BtnPlay_Click(object sender,EventArgs e) {

          IMediaFactory factory = null;

          switch (cbbMediaType.SelectItem.ToString().ToLower()) {

          case ("mp3"):

          factory = new MP3MediaFactory();

          break;

          case ("wav"):

          factory = new WAVMediaFactory();

          break;

          //其他型略;

          }

          IMedia media = factory.CreateMedia();

          media.Play();

          }

          如果你在最開始沒有體會到IMedia接口的好,在里你應該明白了。在工廠中用到了接口;而在主程序中,仍然要使用接口。使用接口有什么好?那就是你的主程序可以在沒有具體業務類候,同可以編譯。因此,即使你增加了新的業務,你的主程序是不用的。

          在看起來,個不用改主程序的理想,依然沒有完成。看到了BtnPlay_Click()中,依然用new建了一些具體例。如果沒有完全和具體分開,一旦更改了具體業務,例如增加了新的工廠,仍然需要改主程序,何況討厭switch句仍然存在,它好像是翅膀上滋生的毒瘤,提示我然翅膀已從僵冷的世界里復活,但雙翅膀是有病的,并不能正常地翔。

          是使用配置文件的候了。我可以把每種媒體文件類類型的相信息放在配置文件中,然后根據配置文件來選擇創建具體的象。并且,象的方法將使用反射來完成。首先,建配置文件:

          <appSettings>

          <add key="mp3" value="WingProject.MP3Factory" />

          <add key="wav" value="WingProject.WAVFactory" />

          <add key="rm" value="WingProject.RMFactory" />

          <add key="mpeg" value="WingProject.MPEGFactory" />

          </appSettings>

          然后,在主程序界面的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 void BtnPlay_Click(object sender,EventArgs e) {

          string mediaType = cbbMediaType.SelectItem.ToString().ToLower();

          string factoryDllName = ConfigurationSettings.AppSettings[mediaType].ToString();

          IMediaFactory factory = (IMediaFactory)Activator.CreateInstance("MediaLibrary",factoryDllName).Unwrap();//MediaLibray引用的媒體文件及工廠的程序集;

          IMedia media = factory.CreateMedia();

          media.Play();

          }

          兒的翅膀不僅僅復活,有了可以的能力;同們還賦雙翅膀更的功能,它可以得更高,得更

          享受自由翔的意吧。想一下,如果我要增加某種媒體文件的播放功能,如AVI文件。那么,我只需要在原來的業務程序集中AVI,并實現IMedia接口,同時繼VideoMedia。另外在工廠業務AVIMediaFactory,并實現IMediaFactory接口。假設這個新的工廠WingProject.AVIFactory在配置文件中添加如下一行:

          <add key="AVI" value="WingProject.AVIFactory" />

          而主程序呢?根本不需要做任何改,甚至不用重新編譯雙翅膀照可以自如地行!

          posted on 2009-08-12 16:40 calvin 閱讀(215) 評論(0)  編輯  收藏 所屬分類: Design Patterns

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 安丘市| 彰化县| 肥乡县| 赞皇县| 潜江市| 河曲县| 兴隆县| 京山县| 吕梁市| 昌邑市| 宜章县| 濮阳市| 新蔡县| 五大连池市| 三原县| 伊宁县| 海伦市| 南皮县| 镇远县| 吴旗县| 丽江市| 永新县| 成安县| 安陆市| 沁水县| 琼结县| 龙南县| 永平县| 施甸县| 临猗县| 津市市| 德令哈市| 长葛市| 永平县| 收藏| 涡阳县| 夏津县| 全州县| 纳雍县| 额尔古纳市| 勃利县|