需求:
公司有两个人分别写了"小狗类"和"小猫类",两个类里都已经写好了小狗和小猫喜欢的食物的方法,主程让你写一个类来输出它们各自喜欢的食物。
首先,我们先来看一看 A 程序员写的"小狗类",里面有个 LikeFood 方法,如下代码所示:
// A程序员写的小狗类
class Dog
{
public void LikeFood()
{
Debug.Log("我是小狗,我喜欢吃肉");
}
}
B 程序员写的"小猫类",里面也有一个 LikeFood 方法,如下代码所示
// B程序员写的小猫类
class Cat
{
public void LikeFood()
{
Debug.Log("我是小猫,我喜欢吃鱼");
}
}
你按照需求写了一个动物园类 Zoo,代码如下所示:
//动物园类
class Zoo
{
public void Show(Dog dog)
{
dog.LikeFood();
}
public void Show(Cat cat)
{
cat.LikeFood();
}
}
在 Start 方法里,并实现了小狗小猫喜欢的食物的功能,代码如下:
void Start()
{
Zoo zoo=new Zoo();
zoo.Show(new Dog());
zoo.Show(new Cat());
}
运行u3d,输出如下图所示:
主程交代的功能完美实现了,这一切工作良好,但好景不长,公司又需要给动物园增加一个猴子,让C程序员去写这个 Monkey 类,并能输出它喜欢的食物。
C 程序员写的"小猴子类",里面同样也有一个 LikeFood 方法,如下代码所示
// C程序员写的小猴子类
class Monkey
{
public void LikeFood()
{
Debug.Log("我是小猴子,我喜欢吃香蕉");
}
}
于是你的 Zoo 类就变成了这样,仅仅多了一个重载方法:
//动物园类
class Zoo
{
public void Show(Dog dog)
{
dog.LikeFood();
}
public void Show(Cat cat)
{
cat.LikeFood();
}
public void Show(Monkey monkey)
{
monkey.LikeFood();
}
}
然后你在 Start 方法里也做了稍微改动,代码如下所示:
void Start()
{
Zoo zoo=new Zoo();
zoo.Show(new Dog());
zoo.Show(new Cat());
zoo.Show(new Monkey());
}
也正确输出了结果,你不禁暗暗得意,一个变化而已,我只需要再Zoo类里增加一个重载方法就好了。
但如果后面还有更多动物需要你输出它们喜欢的食物,你的Zoo类都要修改,这对你来说不是一件好事。”
于是你仔细观察 Zoo 类,发现不变的是 Show 方法,变化的是 Show 方法是参数。因为每个动物都不一样,所以参数也就不一样。所以原来就需要重载多个方法。
如果有一个类,能接收所有动物,那不就解决了?没错,于是你想到了定义一个父类叫 Animal ,里面有个 LikeFood 方法,让所有动物类去继承 Animal 类。
最后你的 Animal 类代码如下:
class Animal
{
public virtual void LikeFood()
{
Debug.Log("我是Animal类");
}
}
注:原文里并没有 virtual 关键字,参考的时候我也没有看出来有什么问题,我自己打印的时候输出的全部是“我是Animal类”,才看出来有问题(每个子类的对象都只是实现了父类的方法,并不是自己的方法)
Zoo 类代码如下:
//动物园类
class Zoo
{
public void Show(Animal animal)
{
animal.LikeFood();
}
}
你告诉A.B.C程序员,让它们写的动物类都去继承Animal(这里我让每个子类的 LikeFood 方法都重写了父类的 LikeFood 方法),并且里面有个输出动物喜欢食物的方法,三人代码如下:
class Dog:Animal
{
public override void LikeFood()
{
Debug.Log("我是小狗,我喜欢吃肉");
}
}
class Cat:Animal
{
public override void LikeFood()
{
Debug.Log("我是小猫,我喜欢吃鱼");
}
}
class Monkey:Animal
{
public override void LikeFood()
{
Debug.Log("我是小猴子,我喜欢吃香蕉");
}
}
运行也一切良好,不管以后还有什么类,只要让需要添加的动物类,继承Animal,并有个l LkeFood 方法,那么你无需修改 Zoo 类,只需要在 Start 方法里传入动物类的实例就能正常工作。
你大赞你聪明绝顶,这样一来,你的 Zoo 类根本不需要改变了。
有一天,公司新来一个程序员D,主程让D写个兔子类,你告诉D,你写的 Rabbit 类必须继承Animal,并且有一个它喜欢的食物的方法。
D按照你要求的,写的兔子类如下:
class Rabbit:Animal
{
public void FavoriteFood()
{
Debug.Log("我是兔子,我喜欢吃萝卜");
}
}
然后你在 Start 方法里又加了一行代码 zoo.Show(new Rabbit());
运行程序后,Raabit 类并没有输出预想的结果,你不得不花了点时间去排查原因,最后你发现这不是什么大问题,因为新来的D虽然写了 Rabbit 类,但里面的方法叫:FavoriteFood()。
因为他并不知道你们之前约定的动物喜欢的食物的函数需要命名为:LikeFood()
但是,为什么会导致这个问题?
那是因为没有一种约束,使得子类继承父类的时候必须实现父类的方法。有没有一个类,能让它的子类必须实现它定义的方法?有,那就是接口。
于是你修改 Animal 类为接口,代码如下:
interface Animal
{
void LikeFood();
}
A,B,C,D程序员的代码修改如下(此时我把父类的virtual和子类的override关键字已经去掉)
class Dog:Animal
{
public void LikeFood()
{
Debug.Log("我是小狗,我喜欢吃肉");
}
}
class Cat:Animal
{
public void LikeFood()
{
Debug.Log("我是小猫,我喜欢吃鱼");
}
}
class Monkey:Animal
{
public void LikeFood()
{
Debug.Log("我是小猴子,我喜欢吃香蕉");
}
}
class Rabbit:Animal
{
public void LikeFood()
{
Debug.Log("我是兔子,我喜欢吃萝卜");
}
}
由于 Animal 接口有个 LikeFood() 方法,那么 Rabbit 子类去实现 Animal 接口必须实现LikeFood(),否则程序不能通过。
如果之前我们以接口的形式写了Animal,D程序员继承了Animal类但却写成Favorite的时候,就会报错。如下图所示:
错误很清楚,不用你事先告诉D程序员需要把方法名写成 LikeFood ,他也会改的。
D改正后代码正常工作,因为Animal是接口,里面有个LikeFood() 方法,以后再添加各种动物进来,只需要实现 Animal 接口,并且也不会出现有的人会因为子类的方法命名问题而导致出错了。
这时你再想,虽然用继承一个普通父类也可以满足要求,但是一个普通父类根本没有约束力
而用了接口就不一样了,子类必须实现父类的所有方法,因为 Zoo 类里调用的是LikeFood(),由于子类必须实现父类,那么所有子类都会有 LikeFood(),你根本不用担心子类有没有这个方法。
所以接口能在多人协作下,定义一系列方法,让子类必须存在接口定义的类,防止在另外的类里调用一个人写的接口的子类时,找不到方法的问题。
如果这篇博客有幸帮到了您,欢迎点击下方链接,和更多志同道合的伙伴一起交流,一起进步。
结束语