<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Calvin's Tech Space

    成于堅(jiān)忍,毀于浮躁

       :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
     

    有了翅膀才能,欠缺靈活的代就象壞了翅膀的兒。不能翔,就少了幾動(dòng)的氣韻。我需要碼帶去溫暖的陽光,僵冷的翅膀重新起來。結(jié)實(shí)例,通過應(yīng)OOP、設(shè)計(jì)模式和重構(gòu),你會(huì)看到代是怎一步一步復(fù)活的。

    了更好的理解設(shè)計(jì)思想,實(shí)例盡可能簡(jiǎn)單化。但隨著需求的增加,程序?qū)⒃絹碓綇?fù)。此時(shí)就有修改設(shè)計(jì)的必要,重構(gòu)和設(shè)計(jì)模式就可以派上用場(chǎng)了。最后當(dāng)設(shè)計(jì)漸趨完美后,你會(huì)發(fā)現(xiàn),即使需求不斷增加,你也可以神清氣,不用碼設(shè)計(jì)煩惱了。

    假定我設(shè)計(jì)一個(gè)媒體播放器。媒體播放器目前只支持音文件mp3wav。如果不談設(shè)計(jì),設(shè)計(jì)出來的播放器可能很簡(jiǎn)單

    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;

    }

    }

    }

    自然,你會(huì)發(fā)現(xiàn)這個(gè)設(shè)計(jì)非常的糟糕。因它根本沒有未來的需求更提供最起擴(kuò)展。如果你的設(shè)計(jì)結(jié)果是這樣,那么當(dāng)你為應(yīng)接不暇的需求更而焦頭爛額時(shí)候,你可能更希望讓這設(shè)計(jì)到它應(yīng)該去的地方,就是桌面的回收站。仔細(xì)分析段代,它其實(shí)是一種最古老的面向結(jié)構(gòu)設(shè)計(jì)。如果你要播放的不僅僅mp3wav,你會(huì)不斷地增加相應(yīng)地播放方法,然后switch子句越來越長(zhǎng),直至達(dá)到你視線看不到的地步。

    好吧,我先來體驗(yàn)對(duì)象的精神。根據(jù)OOP的思想,我們應(yīng)該mp3wav看作是一個(gè)獨(dú)立的對(duì)象。那么是這樣嗎?

    public class MP3 {

    public void Play() {

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

    }

    }

    public class WAV {

    public void Play() {

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

    }

    }

    的,你已經(jīng)知道怎么建立對(duì)象了。更可喜的是,你在不知不應(yīng)用了重構(gòu)的方法,把原來那個(gè)垃圾設(shè)計(jì)中的方法名字改統(tǒng)一的Play()方法。你在后面的設(shè)計(jì)中,會(huì)發(fā)現(xiàn)這樣改名是多么的關(guān)!但似乎你并沒有中要害,以現(xiàn)在的方式去更改MediaPlayer的代實(shí)質(zhì)并沒有多大的化。

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

    public class AudioMedia {

    public void Play() {

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

    }

    }

    現(xiàn)在我引入了承的思想,OOP也算是象模象了。得意之余,認(rèn)真分析現(xiàn)實(shí)世界吧。實(shí)現(xiàn)實(shí)生活中,我播放的只會(huì)是某種具體型的音文件,因此個(gè)AudioMedia并沒有實(shí)際使用的情況。對(duì)應(yīng)設(shè)計(jì)中,就是:個(gè)遠(yuǎn)不會(huì)被實(shí)例化。所以,動(dòng)一下手術(shù),將其改抽象。好了,現(xiàn)在的代有點(diǎn)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();

    }

    }

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

    是不是到此就畫上圓滿的句號(hào)呢?然后刁的客是永遠(yuǎn)不會(huì)滿足的,他在抱怨個(gè)媒體播放器了。因不想在看足球比時(shí)候,只聽到主持人的解,他更渴望看到足球明星在球場(chǎng)奔跑的英姿。也就是,他希望你的媒體播放器能支持視頻文件。你又痛苦了,因在更改硬件設(shè)計(jì)的同時(shí),原來的設(shè)計(jì)結(jié)構(gòu)似乎出了問題。因為視頻文件和音文件有很多不同的地方,你可不能偷懶,讓視頻文件對(duì)認(rèn)文件作父啊。你需要為視頻文件設(shè)計(jì)另外的類對(duì)象了,假設(shè)支持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的子。

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

    public interface IMedia {

    void Play();

    }

    public abstract class AudioMedia:IMedia {

    public abstract void Play();

    }

    public abstract class VideoMedia:IMedia {

    public abstract void Play();

    }

    再更改一下MediaPlayer設(shè)計(jì)OK了:

    public class MediaPlayer {

    public void Play(IMedia media) {

    media.Play();

    }

    }

    現(xiàn)在可以總結(jié)一下,從MediaPlayer的演,我可以得出這樣一個(gè)結(jié)論調(diào)類對(duì)象的屬性和方法時(shí),盡量避免將具體類對(duì)象作為傳遞參數(shù),而應(yīng)傳遞其抽象對(duì)象,更好地是傳遞接口,將實(shí)際調(diào)用和具體對(duì)象完全剝離開,這樣可以提高代的靈活性。

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

    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合框的選項(xiàng),決定播放哪一種文件,然后單擊Play鈕執(zhí)行。

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

    public interface IMediaFactory {

    IMedia CreateMedia();

    }

    然后每種媒體文件對(duì)象搭建一個(gè)工廠,并統(tǒng)實(shí)現(xiàn)工廠接口

    public class MP3MediaFactory:IMediaFactory {

    public IMedia CreateMedia() {

    return new MP3();

    }

    }

    public class RMMediaFactory:IMediaFactory {

    public IMedia CreateMedia() {

    return new RM();

    }

    }

    //其它工廠略;

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

    可能會(huì)有人,即使你使用種方式,那么在判斷具體創(chuàng)建哪個(gè)工廠的時(shí)候,不是也要用到switch?我承認(rèn)這種看法是對(duì)的。使用工廠模式,其直接好并非是要解決switch句的難題,而是要延遲對(duì)象的生成,以保的代的靈活性。當(dāng)然,我有最后一招沒有使出來,到后面你會(huì)發(fā)現(xiàn),switch句其實(shí)會(huì)完全消失。

    有一個(gè)問題,就是真的有必要實(shí)現(xiàn)AudioMediaVideoMedia兩個(gè)抽象類嗎其子直接實(shí)現(xiàn)接口不更簡(jiǎn)單對(duì)于本文提到的需求,我想你是對(duì)的,但不排除AudioMediaVideoMedia們還會(huì)存在區(qū)。例如音文件只需要提供聲卡的接口,而視頻文件需要提供給顯卡的接口。如果MP3、WAV、RM、MPEG直接實(shí)現(xiàn)IMedia接口,而不通AudioMediaVideoMedia,在滿足其它需求的設(shè)計(jì)上也是不合理的。當(dāng)然經(jīng)不包括在本文的范疇了。

    現(xiàn)在主程序界面發(fā)生了稍的改

    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。個(gè)中,實(shí)現(xiàn)Play方法,并根據(jù)傳遞的參數(shù),調(diào)用相應(yīng)媒體文件的Play方法。在沒有工廠對(duì)象的時(shí)候,看起來個(gè)類對(duì)象運(yùn)行得很好。如果是作一個(gè)類庫設(shè)計(jì)者來看,他提供了這樣一個(gè)接口,供主界面程序員調(diào)用。然而在引入工廠模式后,在里面使用MediaPlayer經(jīng)多余了。所以,我住的是,重構(gòu)并不僅僅是往原來的代添加新的內(nèi)容。當(dāng)我們發(fā)現(xiàn)一些不必要的設(shè)計(jì)時(shí)需要果斷地些冗余代

    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();

    }

    如果你在最開始沒有體會(huì)到IMedia接口的好,在里你應(yīng)該經(jīng)明白了。在工廠中用到了接口;而在主程序中,仍然要使用接口。使用接口有什么好?那就是你的主程序可以在沒有具體業(yè)務(wù)類時(shí)候,同可以編譯。因此,即使你增加了新的業(yè)務(wù),你的主程序是不用動(dòng)的。

    ,現(xiàn)在看起來,個(gè)不用改動(dòng)主程序的理想,依然沒有完成??吹搅?/span>BtnPlay_Click()中,依然用new創(chuàng)建了一些具體實(shí)例。如果沒有完全和具體分開,一旦更改了具體業(yè)務(wù),例如增加了新的工廠,仍然需要改主程序,何況討厭switch句仍然存在,它好像是翅膀上滋生的毒瘤,提示我,然翅膀已經(jīng)從僵冷的世界里復(fù)活,但雙翅膀是有病的,并不能正常地翔。

    是使用配置文件的時(shí)候了。我可以把每種媒體文件類類型的相應(yīng)信息放在配置文件中,然后根據(jù)配置文件來選擇創(chuàng)建具體的對(duì)象。并且,創(chuàng)對(duì)象的方法將使用反射來完成。首先,創(chuàng)建配置文件:

    <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();

    }

    現(xiàn)兒的翅膀不僅僅復(fù)活,有了可以的能力;同時(shí)們還賦雙翅膀更強(qiáng)的功能,它可以得更高,得更遠(yuǎn)

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

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

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

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

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 在线亚洲精品福利网址导航| 91九色老熟女免费资源站| 一级做a毛片免费视频| 看全免费的一级毛片| 亚洲AV无码专区在线电影成人| 亚洲国产日韩a在线播放| 亚洲精品无码专区在线播放| www.av在线免费观看| 免费一级全黄少妇性色生活片| 亚洲AV无码成人精品区狼人影院 | 女人被免费视频网站| 18禁成年无码免费网站无遮挡| 插B内射18免费视频| 在线免费观看一级毛片| 精品国产麻豆免费网站| 国产一级大片免费看| 亚洲精品国产高清不卡在线| 伊人久久综在合线亚洲91| 亚洲国产精品国自产拍AV| 久久久久久亚洲Av无码精品专口 | 亚洲无线观看国产精品| 国产亚洲人成无码网在线观看| 亚洲av永久无码精品国产精品| 在线观看亚洲人成网站| 7777久久亚洲中文字幕| 激情无码亚洲一区二区三区 | 亚洲最大av无码网址| 中文字幕人成人乱码亚洲电影| 久久久久亚洲av无码尤物| 亚洲视频免费在线看| 亚洲国产区男人本色在线观看| 国产成人精品亚洲| 国产一级一毛免费黄片| 亚洲视频免费在线看| 老司机永久免费网站在线观看| 亚洲人成网站色在线入口| 亚洲国产成人久久精品影视| 久久亚洲精品专区蓝色区| 免费播放国产性色生活片| 久久黄色免费网站| 最近免费中文字幕4|