论文部分内容阅读
[摘 要]:2005年初,笔者在北京参加了北大青鸟教师培训班,学习的内容是C#。之后,经过几年的教学实践,笔者对面向对象程序设计有了更为深广的理解,本文笔者就谈谈自己的认识与体会,以与中职计算机专业学生朋友交流。
[关键词]:中职生 面向对象程序设计 C#
首先,笔者谈几点理解面向对象程序设计的要点:
封装性:数据和行为封装在一个自定义的数据类型里面,其作用:(1)把松散的方法聚合到数据类型里面。(2)隐藏数据类型内部的数据定义。
继承性:把多个数据类型按照一定的方法进行抽象,把共有的属性放的一个高层的数据类型里面,然后底层的多个类型可以共享这个高层类型的数据和方法。作用有两个:(1)简化数据类型的定义,减少重复定义。(2)使组织结构清晰化,这和现实世界的分门别类具有异曲同工之妙。
多态性:一个方法,可以根据其操纵的具体类型的对象的不同(抽象类型相同),可以有不同的表现。其作用:(1)进一步简化方法的定义。(2)是多种不同的方法具有统一的接口,方便调用。
接下来,我们结合实例说说设计模式。
为了更好地理解设计思想,实例尽可能简单化。但随着需求的增加,程序将越来越复杂。此时,就有修改設计的必要,重构和设计模式就可以派上用场了。最后当设计渐趋完美后,你会发现,即使需求不断增加,你也可以神清气闲,不用为代码设计而烦恼了。
假定我们要设计一个媒体播放器。该媒体播放器目前只支持音频文件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子句越来越长,直至达到你视线看不到的地步。
我们来体验面向对象的思想。把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.");
}
}
现在,我们引入了继承的思想。
我们播放的只会是某种具体类型的音频文件,因此,这个AudioMedia类并没有实际使用的情况。对应在设计中,就是:这个类永远不会被实例化。所以,还得动一下手术,将其改为抽象类。
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.");
}
}
虽然视频和音频格式不同,别忘了,他们都是媒体中的一种,很多时候,他们有许多相似的功能,比如播放。根据接口的定义,完全可以将相同功能的一系列对象实现同一个接口:
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类的演变,我们可以得出这样一个结论:在调用类对象的属性和方法时,尽量避免将具体类对象作为传递参数,而应传递其抽象对象,更好地是传递接口,将实际的调用和具体对象完全剥离开,这样可以提高代码的灵活性。
[关键词]:中职生 面向对象程序设计 C#
首先,笔者谈几点理解面向对象程序设计的要点:
封装性:数据和行为封装在一个自定义的数据类型里面,其作用:(1)把松散的方法聚合到数据类型里面。(2)隐藏数据类型内部的数据定义。
继承性:把多个数据类型按照一定的方法进行抽象,把共有的属性放的一个高层的数据类型里面,然后底层的多个类型可以共享这个高层类型的数据和方法。作用有两个:(1)简化数据类型的定义,减少重复定义。(2)使组织结构清晰化,这和现实世界的分门别类具有异曲同工之妙。
多态性:一个方法,可以根据其操纵的具体类型的对象的不同(抽象类型相同),可以有不同的表现。其作用:(1)进一步简化方法的定义。(2)是多种不同的方法具有统一的接口,方便调用。
接下来,我们结合实例说说设计模式。
为了更好地理解设计思想,实例尽可能简单化。但随着需求的增加,程序将越来越复杂。此时,就有修改設计的必要,重构和设计模式就可以派上用场了。最后当设计渐趋完美后,你会发现,即使需求不断增加,你也可以神清气闲,不用为代码设计而烦恼了。
假定我们要设计一个媒体播放器。该媒体播放器目前只支持音频文件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子句越来越长,直至达到你视线看不到的地步。
我们来体验面向对象的思想。把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.");
}
}
现在,我们引入了继承的思想。
我们播放的只会是某种具体类型的音频文件,因此,这个AudioMedia类并没有实际使用的情况。对应在设计中,就是:这个类永远不会被实例化。所以,还得动一下手术,将其改为抽象类。
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.");
}
}
虽然视频和音频格式不同,别忘了,他们都是媒体中的一种,很多时候,他们有许多相似的功能,比如播放。根据接口的定义,完全可以将相同功能的一系列对象实现同一个接口:
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类的演变,我们可以得出这样一个结论:在调用类对象的属性和方法时,尽量避免将具体类对象作为传递参数,而应传递其抽象对象,更好地是传递接口,将实际的调用和具体对象完全剥离开,这样可以提高代码的灵活性。