面向对象设计(OOD)思想——还是以播放器为例(ZT) >nQyF
: qRT9n$
有了思想才能飞翔,缺乏灵活就象少了轮子的汽车,难以飞奔。为了更好的理解设计思想,结合一个尽可能简洁的实例来说明OOD、设计模式及重构。通过下面的代码,详细地阐述面向对象设计思想。 .h,xBT`}Ji
一、传统过程化设计思想 KU,w9<~i(
假定我们要设计一个媒体播放器(只从软件设计的角度,不涉及硬件)。该媒体播放器目前只支持音频文件mp3和wav。按照结构化设计思想,设计出来的播放器的代码如下: tHbPd.^
public class MediaPlayer 9eiBj
{ l,wN@Nk
private void PlayMp3() N_D+d4@
{ 2(Uz9!<V
MessageBox.Show("Play the mp3 file."); 2-aYqMmT;
} sv"mba.J
M%xL K7
private void PlayWav() s2~dmZ_B|_
{ *GP_ut%
MessageBox.Show("Play the wav file."); GDp p`'\
} !T#y r)
OL5HofgNm
public void Play(string audioType) )H)Udhz
{ CDnz
&?
switch (audioType.ToLower()) /T[ICd2J
{ CDj Dhs
case ("mp3"): e"#D){k#
PlayMp3(); 4Z9wzQ>
break; ~+C?][T
case ("wav"): 8"mW!M
PlayWav(); f<Tz#w&6W
break; a
+yI2s4Z
} !m(L0YH
} I^(#\vRW
} Aq%^>YAp
从传统的过程化设计思想来看,这是一段既实用又简洁的代码。 @T1+b"TC
如果,客户又提出新的要求:要播放器不仅仅播放mp3和wav文件,还要播放其他音频文件如wma、mp4等,为此我们要不断地增加相应地播放方法和修改条件语句,直止条件语句足够长。 Z&jb,eh2
如果,客户感到这个媒体播放器功能太少了,只能闻其声,不能见其人,太单一。如果在听着优美音乐的同时又能看到歌唱者潇洒、英俊的舞姿那就更好了。从代码设计的角度看,他们希望媒体播放器支持视频文件了。也许你会想,不会再增加视频这方面的代码,可以,在增加视频媒体的播放方法,在修改条件判断语句,如果还有其他,还可以同样地增加、修改。到此你也许会提出,要是不修改或很少修改原来的代码就能增添其他功能该多好啊! '-33iG
这样看,原来的软件设计结构似乎有点问题。事实上,随着功能的不断增加,你越来越发现这个设计非常的糟糕,因为它根本没有为未来的需求变更提供最起码的扩展。为了应接不暇的变更需求,你不得不不厌其烦地修改原来的代码,使其适应需求变化,甚至在修改代码时,由于过多的代码依赖关系弄得人焦头烂额,直止一塌糊涂。 ?i2Wst
二、面向对象设计思想 wg<|@z5
还是以设计一个媒体播放器为例,设计要求相同。不访我们换个设计思路利用面向对象设计思想(OOD)来做做看如何! m,C,<I|'d
根据OOD的思想,我们应该把mp3和wav分别看作是两个独立的对象。代码设计如下: E5G"QnxR>N
public class MP3 vUe
*
{ FK# E7
K
public void Play() I0+wczW,^
{ 1xAFu+
MessageBox.Show("Play the mp3 file."); %aBJ+V F
} :gscW&k
} @B[Cc`IN"
]^jdO# #M
public class WAV (%y c5+f!
{ cvE.r330|
public void Play() =4<S8Cp
{ UvJuOh+
MessageBox.Show("Play the wav file."); Du:p!nO
} :dIQV(iW
} ~+ s*\~
^y0C5Bl;
Public class MediaPlayer
G].__]
{ s^YTI\L
\
switch (audioType.ToLower()) lkH;N<U
{ $KWYe{#
case ("mp3"): C]yQ "b
MP3 m = new MP3(); <o!&Kk 9
m.Play(); |\|)j>[i
break; b>=Wq
case ("wav"): >q@Sd
WAV w = new WAV(); MiH}VfI
w.Play(); 6w"( y~c1
break; @D~+D@i$TW
} M*|VLOo=v
} 'K`Rbhy
现在我们重构代码,建立统一的Play()方法,(在后面的设计中,你会发现这样改名是多么的重要!)更改媒体播放类MediaPlayer的代码。如果这样的设计代码,实质上没有多大的变化,只是对原来过程化设计思想的一种替代,并没有击中要害,亦然没有灵活性、可扩展性。 ~,*YmB=Z
2.1单向分派技术的应用(在这里用类的多态来实现的) T<+ht8&M8
我们不访这样设想:既然mp3和wav都属于音频文件,都具有音频文件的共性,应该建立一个共同的AudioMedia父类。 I+"?,Ej$K
public class AudioMedia
$.Q>M]xH
{ N^
s!!Sbpq
public void Play() p&sK\
{ VkDS&g~Ws
MessageBox.Show("Play the AudioMedia file."); (y~laW!
} MATgJ`lsy
} !3I(4?G,
现在引入继承思想,OOD就有点雏形了(不是说有了继承就有了OOD思想,这里只是从继承的角度谈一谈OOD思想,当然从其他角度如合成、聚合等角度也能很好地体现OOD思想)。 daB l%a=
其实在现实生活中,我们的播放器播放的只能是某种具体类型的音频文件如mp3,因此这个AudioMedia类只能是音频媒体的一个抽象化概念,并没有实际的使用情况。对应在OOD设计中,既这个类永远不会被实例化。为此我们应将其改为抽象类,如下: 8HFXxpt[G
public abstract class AudioMedia -*%!q$:
{ 6UW:l|}4#2
public abstract void Play(); 9Ue7
~"=
} uR:=V9O
Yi&-m}
public class MP3:AudioMedia m
io1kDq<
{ =^Sw*[eiy
public override void Play() Bhu@ 2KdA
{ w;c#drY7S
MessageBox.Show("Play the mp3 file."); 2zKo
} z_Wm
HB
} Yn4)Zhkk
,<$YVXe/
public class WAV:AudioMedia n{^<&GWox
{ (7;J"2M
public override void Play() q11QAx4p
{ uKbHFF
MessageBox.Show("Play the wav file."); @q+cmJKv
} j&dx[4|m:h
} vS$oT]-hKE
&{zwM |Q@?
public class MediaPlayer &IRA=nJ
{ ZUXse1,
//根据需要完成任务的单向分派 4e+BqCriC*
public void Play(AudioMedia media) *5y
W
{ n{64g+
media.Play(); V~T`&
} '<%Nw-
} "*w)puD
到此,我们通过单向分派技术使OOD思想得到进一步的体现。现在的设计,即满足了类之间的层次关系,又保证了类的最小化原则,同时又体现了面向对象设计原则(开—闭原则、里氏代换原则)更利于扩展。(止此,你会发现play方法名的更改是多么必要)。 j,=*WG
如果现在又增加了对WMA、MP4等音频文件的播放,只需要设计WMA类,MP4类,并继承AudioMedia,在相应的子类中重写Play方法就可以了,MediaPlayer类对象的Play方法根本不用任何改变。 ?""\
如果让媒体播放器能够支持视频文件,必须另外设计视频媒体的类。因视频文件和音频文件有很多不同的地方,不可能让视频继承音频。假设我们播放器支持RM和MPEG格式的视频。视频类代码如下: F_nZvv[H?
public abstract class VideoMedia t=Z&