您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > 浅谈C多态的魅力(虚方法,抽象,接口实现)
浅谈C#多态的魅力(虚方法,抽象,接口实现)前言:我们都知道面向对象的三大特性:封装,继承,多态。封装和继承对于初学者而言比较好理解,但要理解多态,尤其是深入理解,初学者往往存在有很多困惑,为什么这样就可以?有时候感觉很不可思议,由此,面向对象的魅力体现了出来,那就是多态,多态用的好,可以提高程序的扩展性。常用的设计模式,比如简单工厂设计模式,核心就是多态。其实多态就是:允许将子类类型的指针赋值给父类类型的指针。也就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。如果这边不理解可以先放一放,先看下面的事例,看完之后再来理解这句话,就很容易懂了。理解多态之前首先要对面向对象的里氏替换原则和开放封闭原则有所了解。里氏替换原则(LiskovSubstitutionPrinciple):派生类(子类)对象能够替换其基类(超类)对象被使用。通俗一点的理解就是“子类是父类”,举个例子,“男人是人,人不一定是男人”,当需要一个父类类型的对象的时候可以给一个子类类型的对象;当需要一个子类类型对象的时候给一个父类类型对象是不可以的!开放封闭原则(OpenClosedPrinciple):封装变化、降低耦合,软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。因此,开放封闭原则主要体现在两个方面:对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。对这两个原则有一定了解之后就能更好的理解多态。首先,我们先来看下怎样用虚方法实现多态。我们都知道,喜鹊(Magpie)、老鹰(Eagle)、企鹅(Penguin)都是属于鸟类,我们可以根据这三者的共有特性提取出鸟类(Bird)做为父类,喜鹊喜欢吃虫子,老鹰喜欢吃肉,企鹅喜欢吃鱼。创建基类Bird如下,添加一个虚方法Eat():///summary///鸟类:父类////summarypublicclassBird{///summary///吃:虚方法////summarypublicvirtualvoidEat(){Console.WriteLine(我是一只小小鸟,我喜欢吃虫子~);}}创建子类Magpie如下,继承父类Bird,重写父类Bird中的虚方法Eat():///summary///喜鹊:子类////summarypublicclassMagpie:Bird{///summary///重写父类中Eat方法////summarypublicoverridevoidEat(){Console.WriteLine(我是一只喜鹊,我喜欢吃虫子~);}}创建一个子类Eagle如下,继承父类Bird,重写父类Bird中的虚方法Eat():///summary///老鹰:子类////summarypublicclassEagle:Bird{///summary///重写父类中Eat方法////summarypublicoverridevoidEat(){Console.WriteLine(我是一只老鹰,我喜欢吃肉~);}}创建一个子类Penguin如下,继承父类Bird,重写父类Bird中的虚方法Eat():///summary///企鹅:子类////summarypublicclassPenguin:Bird{///summary///重写父类中Eat方法////summarypublicoverridevoidEat(){Console.WriteLine(我是一只小企鹅,我喜欢吃鱼~);}}到此,一个基类,三个子类已经创建完毕,接下来我们在主函数中来看下多态是怎样体现的。staticvoidMain(string[]args){//创建一个Bird基类数组,添加基类Bird对象,Magpie对象,Eagle对象,Penguin对象Bird[]birds={newBird(),newMagpie(),newEagle(),newPenguin()};//遍历一下birds数组foreach(Birdbirdinbirds){bird.Eat();}Console.ReadKey();}运行结果:由此可见,子类Magpie,Eagle,Penguin对象可以赋值给父类对象,也就是说父类类型指针可以指向子类类型对象,这里体现了里氏替换原则。父类对象调用自己的Eat()方法,实际上显示的是父类类型指针指向的子类类型对象重写父类Eat后的方法。这就是多态。多态的作用到底是什么呢?其实多态的作用就是把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。以上程序也体现了开放封闭原则,如果后面的同事需要扩展我这个程序,还想再添加一个猫头鹰(Owl),很容易,只需要添加一个Owl类文件,继承Bird,重写Eat()方法,添加给父类对象就可以了。至此,该程序的扩展性得到了提升,而又不需要查看源代码是如何实现的就可以扩展新功能。这就是多态带来的好处。我们再来看下利用抽象如何来实现多态。还是刚才的例子,我们发现Bird这个父类,我们根本不需要使用它创建的对象,它存在的意义就是供子类来继承。所以我们可以用抽象类来优化它。我们把Bird父类改成抽象类,Eat()方法改成抽象方法。代码如下:///summary///鸟类:基类////summarypublicabstractclassBird{///summary///吃:抽象方法////summarypublicabstractvoidEat();}抽象类Bird内添加一个Eat()抽象方法,没有方法体。也不能实例化。其他类Magpie,Eagle,Penguin代码不变,子类也是用override关键字来重写父类中抽象方法。Main主函数中Bird就不能创建对象了,代码稍微修改如下:staticvoidMain(string[]args){//创建一个Bird基类数组,添加Magpie对象,Eagle对象,Penguin对象Bird[]birds={newMagpie(),newEagle(),newPenguin()};//遍历一下birds数组foreach(Birdbirdinbirds){bird.Eat();}Console.ReadKey();}执行结果:由此可见,我们选择使用虚方法实现多态还是抽象类抽象方法实现多态,取决于我们是否需要使用基类实例化的对象.比如说现在有一个Employee类作为基类,ProjectManager类继承自Employee,这个时候我们就需要使用虚方法来实现多态了,因为我们要使用Employee创建的对象,这些对象就是普通员工对象。再比如说现在有一个Person类作为基类,Student,Teacher类继承Person,我们需要使用的是Student和Teacher创建的对象,根本不需要使用Person创建的对象,所以在这里Person完全可以写成抽象类。总而言之,是使用虚方法,或者抽象类抽象方法实现多态,视情况而定,什么情况?以上我说的两点~接下来~~~~我要问一个问题,喜鹊和老鹰都可以飞,这个飞的能力,我怎么来实现呢?XXX答:“在父类Bird中添加一个Fly方法不就好了~~”我再问:“好的,照你说的,企鹅继承父类Bird,但是不能企鹅不能飞啊,这样在父类Bird中添加Fly方法是不是不合适呢?”XXX答:“那就在能飞的鸟类中分别添加Fly方法不就可以了吗?”对,这样是可以,功能完全可以实现,可是这样违背了面向对象开放封闭原则,下次我要再扩展一个鸟类比如猫头鹰(Owl),我还要去源代码中看下Fly是怎么实现的,然后在Owl中再次添加Fly方法,相同的功能,重复的代码,这样是不合理的,程序也不便于扩展;其次,如果我还要添加一个飞机类(Plane),我继承Bird父类,合适吗?很显然,不合适!所以我们需要一种规则,那就是接口了,喜鹊,老鹰,飞机,我都实现这个接口,那就可以飞了,而企鹅我不实现这个接口,它就不能飞~~好,接下来介绍一下接口如何实现多态~添加一个接口IFlyable,代码如下:///summary///飞接口////summarypublicinterfaceIFlyable{voidFly();}喜鹊Magpie实现IFlyable接口,代码如下:///summary///喜鹊:子类,实现IFlyable接口////summarypublicclassMagpie:Bird,IFlyable{///summary///重写父类Bird中Eat方法////summarypublicoverridevoidEat(){Console.WriteLine(我是一只喜鹊,我喜欢吃虫子~);}///summary///实现IFlyable接口方法////summarypublicvoidFly(){Console.WriteLine(我是一只喜鹊,我可以飞哦~~);}}老鹰Eagle实现IFlyable接口,代码如下:///summary///老鹰:子类实现飞接口////summarypublicclassEagle:Bird,IFlyable{///summary///重写父类Bird中Eat方法////summarypublicoverridevoidEat(){Console.WriteLine(我是一只老鹰,我喜欢吃肉~);}///summary///实现IFlyable接口方法////summarypublicvoidFly(){Console.WriteLine(我是一只老鹰,我可以飞哦~~);}}在Main主函数中,创建一个IFlyable接口数组,代码实现如下:staticvoidMain(string[]args){//创建一个IFlyable接口数组,添加Magpie对象,Eagle对象IFlyable[]flys={newMagpie(),newEagle()};//遍历一下flys数组foreach(IFlyableflyinflys){fly.Fly();}Console.ReadKey();}执行结果:由于企鹅Penguin没有实现IFlyable接口,所以企鹅不能对象不能赋值给IFlyable接口对象,所以企鹅,不能飞~好了,刚才我提到了飞机也能飞,继承Bird不合适的问题,现在有了接口,这个问题也可以解决了。如下,我添加一个飞机Plane类,实现IFlyable接口,代码如下:///summary///飞机类,实现IFlyable接口////summarypublicclassPlane:IFlyable{///summary///实现接口方法////summarypublicvoidFly(){Console.WriteLine(我是一架飞机,我也能飞~~);}}在Main主函数中,接口IFlyable数组,添加Plane对象:classProgram{staticvoidMain(string[]args){//创建一个IFlyable接口数组,添加Magpie对象,Eagle对象,Plane对象IFlyable[]flys={newMagpie(),newEagle(),newPlane()};//遍历一下flys数组foreach(IFlyableflyinflys){fly.Fly();}Console.ReadKey();}}执行结果:由此,可以看出用接口实现多态程序的扩展性得到了大大提升,以后不管是再扩展一个蝴蝶(Butterfly),还是鸟人(Birder)创建一个类,实现这个接口,在主函数中添加该对象就可以了。也不需要查看源代码是如何实现的
本文标题:浅谈C多态的魅力(虚方法,抽象,接口实现)
链接地址:https://www.777doc.com/doc-2268387 .html