编程思想从实例谈面向对象编程(OOP)、工厂模式和重构

上传人:枫** 文档编号:493180805 上传时间:2023-01-07 格式:DOC 页数:9 大小:131KB
返回 下载 相关 举报
编程思想从实例谈面向对象编程(OOP)、工厂模式和重构_第1页
第1页 / 共9页
编程思想从实例谈面向对象编程(OOP)、工厂模式和重构_第2页
第2页 / 共9页
编程思想从实例谈面向对象编程(OOP)、工厂模式和重构_第3页
第3页 / 共9页
编程思想从实例谈面向对象编程(OOP)、工厂模式和重构_第4页
第4页 / 共9页
编程思想从实例谈面向对象编程(OOP)、工厂模式和重构_第5页
第5页 / 共9页
点击查看更多>>
资源描述

《编程思想从实例谈面向对象编程(OOP)、工厂模式和重构》由会员分享,可在线阅读,更多相关《编程思想从实例谈面向对象编程(OOP)、工厂模式和重构(9页珍藏版)》请在金锄头文库上搜索。

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, 在满足其它需求的设计上也是不合理的。当然这已经不包括在本文的范畴了。现在主程序界面发

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 解决方案

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号