前往顾页
以后地位: 主页 > 收集编程 > .Net实例教程 >

从实例谈Adapter形式

时候:2012-01-14 01:40来源:知行网www.zhixing123.cn 编辑:麦田守望者

有了翅膀才气飞,完善矫捷的代码就象冻坏了翅膀的鸟儿。不克不及翱翔,就少了多少灵动的气韵。我们需求给代码带去暖和的阳光,让僵冷的翅膀从头飞起来。连络实例,经由过程利用OOP、设想形式和重构,你会看到代码是怎样一步一步重生的。

为了更好的了解设想思惟,实例尽可能简朴化。但跟着需求的增加,法度将愈来愈复杂。此时就有点窜设想的需求,重构和设想形式便可以派上用处了。最后当设想渐趋完美后,你会发明,即便需求不竭增加,你也能够神清气闲,不消为代码设想而烦恼了。

假定我们要设想一个媒体播放器。该媒体播放器目前只支撑音频文件mp3和wav。如果不谈设想,设想出来的播放器可能很简朴:
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;
}
}
}

自然,你会发明这个设想非常的糟。因为它底子没无为将来的需求变动供应最起码的扩展。如果你的设想成果是如许,那么当你为目不暇接的需求变动而焦头烂额的时候,你可能更希望让这份设想到它应当去的处所,就是桌面的收受领受站。细心阐发这段代码,它实际上是一种最陈腐的面向布局的设想。如果你要播放的不但仅是mp3和wav,你会不竭地增加呼应地播放体例,然后让switch子句愈来愈长,直至到达你视野看不到的境地。

好吧,我们先来体验工具的精神。按照OOP的思惟,我们应当把mp3和wav看作是一个自力的工具。那么是如许吗?
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的代码,本色并没有多年夜的转变。

既然mp3和wav都属于音频文件,他们都具有音频文件的个性,为甚么不为它们建立一个共同的父类呢?
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体例底子不消改变。

是不是是到此就该画上美满的句号呢?然后刁钻的客户是永久不会满足的,他们在抱怨这个媒体播放器了。因为他们不想在看足球比赛的时候,只听到掌管人的讲解,他们更巴望看到足球明星在球场奔驰的英姿。也就是说,他们希望你的媒体播放器可以或许支撑视频文件。你又该痛苦了,因为在变动硬件设想的同时,本来的软件设想布局仿佛出了问题。因为视频文件和音频文件有很多不合的处所,你可不克不及偷懒,让视频文件工具认音频文件作父亲啊。你需求为视频文件设想别的的类工具了,假定我们支撑RM和MPEG格局的视频:
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按钮履行。

现在该设想形式粉墨登场了,这类按照不合环境建立不合范例的体例,工厂形式是最特长的。先看看我们的工厂需求生产哪些产品呢?固然这里有两种不合范例的媒体AudioMedia和VideoMedia(今后可能更多),但它们同时又都实现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();
}
}
//别的工厂略;

写到这里,或许有人会问,为甚么不直接给AudioMedia和VideoMedia类搭建工厂呢?很简朴,因为在AudioMedia和VideoMedia中,别离另有不合的范例派生,如果为它们搭建工厂,则在CreateMedia()体例中,仍然要利用Switch语句。并且既然这两个类都实现了IMedia接口,可以以为是一种范例,为甚么还要那么费事去请动笼统工厂形式,来天生两类产品呢?

可能还会有人问,即便你利用这类体例,那么在判定详细建立哪个工厂的时候,不是也要用到switch语句吗?我承认这类观点是对的。不过利用工厂形式,其直接好处并不是是要处理switch语句的困难,而是要延迟工具的天生,以包管的代码的矫捷性。当然,我另有最后一招杀手锏没有使出来,到前面你会发明,switch语句其实会完整消逝。

另有一个问题,就是真的有需求实现AudioMedia和VideoMedia两个笼统类吗?让其子类直接实现接口不更简朴?对本文提到的需求,我想你是对的,但不解除AudioMedia和VideoMedia它们还会存在辨别。比方音频文件只需求供应给声卡的接口,而视频文件还需求供应给显卡的接口。如果让MP3、WAV、RM、MPEG直接实现IMedia接口,而不经由过程AudioMedia和VideoMedia,在满足别的需求的设想上也是不公道的。当然这已不包含在本文的范围了。

顶一下
(1)
100%
踩一下
(0)
0%
------分开线----------------------------
标签(Tag):Adapter形式
------分开线----------------------------
颁发评论
请自发遵循互联网相关的政策法规,严禁公布色情、暴力、革命的谈吐。
评价:
神色:
考证码:点击我更换图片
猜你感兴趣