接口
注:本篇文字约4300字,可能花费10分钟。
先不讲开发中为什么要使用接口?有什么好处?
假设你是一个修水管的工人,有一个客户让你装水管,但是客户喜欢管子是三角形的。
很熟练的你就将水管安装到墙上,如图:

过几天,客户又来找你,他觉得三角形的不好看,要让你把三角形的管子,换成正方形的,你不得不还,因为顾客是上帝,(你会觉得为什么一开始不说要用正方形的管子呢?因为需求一直在变化。。。)如图:

哔哩啪啦的累的满头大汗,花费2 3个小时才将管子换成正方形的。没过多久,客户又来找你,这次客户想要换个椭圆形的管子。虽然很无奈,顾客是上帝,又花费几个小时换好管子,如图:

这时你可能会想,为什么换不同形状的水管,需要大动干戈一番呢?刚开始的时候,可以在墙上设计一个固定的水管,并且是圆形的,根据客户的喜好更换不同的水管。这样,以后都不用去动墙上的水管了。这个方法好!~~这就叫做接口,如图:

这里,我查阅了一下百度百科给接口最权威的定义。
接口:是指定一组函数成员而不实现成员的引用类型,其他类型-类和结构可以实现接口。
换句话说:接口没有具体实现,他只是一种行为约束规范,需要子类继承该接口来实现方法。
这就是为什么小白们会觉得接口什么都不做,只定义一个接口,没有任何实现,那不多此一举么?
下面我们在代码中体现接口的作用,再次声明,不讨论场景是否合情合理~~~
需求:公司有两个人分别写了两个国家的母语类,来输出该国的母语,你来负责调用他们写好的类。
1 class China //中国
2 {
3 public void Speak()
4 {
5 Console.WriteLine("我们国家说中国话!~");
6 }
7 }
8 class America //美国
9 {
10 public void Speak()
11 {
12 Console.WriteLine("我们国家说英语!~");
13 }
14 }
你写的Country类
1 class Country
2 {
3 public void show(China china)
4 {
5 china.Speak();
6 }
7 public void show(America america)
8 {
9 america.Speak();
10 }
11 }
调用
1 static void Main(string[] args)
2 {
3 Country c = new Country();
4 c.show(new China());
5 c.show(new America());
6 Console.ReadKey();
7 }
过了一段时间,公司业务变了,需要增加一个俄罗斯类,暂且叫C吧,让C去写这个,并调用
1 class China //中国
2 {
3 public void Speak()
4 {
5 Console.WriteLine("我们国家说中国话!~");
6 }
7 }
8 class America //美国
9 {
10 public void Speak()
11 {
12 Console.WriteLine("我们国家说英语!~");
13 }
14 }
15 class Russia //俄罗斯
16 {
17 public void Speak()
18 {
19 Console.WriteLine("我们国家说俄语!~");
20 }
21 }
于是你的Country类,又多了一个方法重载:
1 class Country
2 {
3 public void show(China china)
4 {
5 china.Speak();
6 }
7 public void show(America america)
8 {
9 america.Speak();
10 }
11 public void show(Russia russia)
12 {
13 russia.Speak();
14 }
15 }
调用
1 static void Main(string[] args)
2 {
3 Country c = new Country();
4 c.show(new China()); //我们国家说中国话!~
5 c.show(new America()); //我们国家说英语!~
6 c.show(new Russia()); //我们国家说俄语!~
7 Console.ReadKey();
8 }
细心的你已经发现,多一个类,就多一个方法的重载,世界上还有怎么多的国家,以后增加一个,Country类就要修改一次,显然不是什么好事!
我们仔细观察Country类,不变的是show方法,变化的是show方法中的参数,如果有那么一个类,能接收所有世界各国,问题不就解决了嘛?聪明的你可能想到了重载,定义一个Person父类,让子类去继承,里面有个show方法。
终极代码
1 static void Main(string[] args)
2 {
3 Country c = new Country();
4 c.show(new China()); //我们国家说中国话!~
5 c.show(new America()); //我们国家说英语!~
6 c.show(new Russia()); //我们国家说俄语!~
7 Console.ReadKey();
8 }
9 class China : Person //中国
10 {
11 public China() : base() //子类构造方法需要调用父类同样参数类型的构造函数,用base关键字代表父类
12 {
13
14 }
15 public override void Speak() //注意增加了override,表示方法重写
16 {
17 Console.WriteLine("我们国家说中国话!~");
18 }
19 }
20 class America : Person //美国
21 {
22 public America() : base() //子类构造方法需要调用父类同样参数类型的构造函数,用base关键字代表父类
23 {
24
25 }
26 public override void Speak() //注意增加了override,表示方法重写
27 {
28 Console.WriteLine("我们国家说英语!~");
29 }
30 }
31 class Russia:Person //俄罗斯
32 {
33 public Russia() : base() //子类构造方法需要调用父类同样参数类型的构造函数,用base关键字代表父类
34 {
35
36 }
37 public override void Speak() //注意增加了override,表示方法重写
38 {
39 Console.WriteLine("我们国家说俄语!~");
40 }
41 }
42 class Country
43 {
44 public void show(Person person)
45 {
46 person.Speak();
47 }
48 }
49 class Person
50 {
51 public virtual void Speak() // 注意修饰符中增加了一个virtual,它表示此方法是虚方法,可以被子类重写
52 {
53 Console.WriteLine("我是Person父类");
54 }
55 }
不管以后还有什么国家的类,只要让需要添加的国家类,继承Person,并且有个Speak方法,那么就不用修改Country类了,只需要传入一个国家类的实例即可。
有一天,公司新来一个人,暂且叫D吧,现在让D写一个France(法国类),并且继承Person类,里面有个母语的方法。
D写的类如下:
1 class France : Person //法国
2 {
3 public France() : base()
4 {
5
6 }
7 public void MotherLanguage()
8 {
9 Console.WriteLine("我们国家说法语!~");
10 }
11 }
调用
1 static void Main(string[] args)
2 {
3 Country c = new Country();
4 c.show(new China()); //我们国家说中国话!~
5 c.show(new America()); //我们国家说英语!~
6 c.show(new Russia()); //我们国家说俄语!~
7 c.show(new France()); //我是Person父类
8 Console.ReadKey();
9 }
很显然不是我们想要的输出结果,你不得不花点时间去排查原因,最后你发现原来D虽然写了。
France类,但是他并不知道之前约定的命名为:Speak()
D写的France类中,里面的方法是:MotherLanguage()
细心的童鞋们已经发现问题的关键了,虽然D继承了Person类,但是没有一种约束,使其继承父类的时候必须实现父类中的方法。有没有一个类,能让它的子类必须实现它定义的方法???
有,那就是接口。
接口如下:
1 interface Person
2 {
3 void Speak();
4 }
由于Person接口有一个Speak()方法,所有子类继承接口的类,必须实现该方法,否则程序不能通过。
这时你再想想,虽然继承一个父类也可以满足要求,但是一个父类根本没有约束力,而接口就不一样了,子类继承接口,必须实现接口中的所有方法。
在多人协作下,定义一系列方法,让子类必须继承该接口,防止在调用一个人写的子类时,找不到方法。
最终完整代码
1 static void Main(string[] args)
2 {
3 Country c = new Country();
4 c.show(new China()); //我们国家说中国话!~
5 c.show(new America()); //我们国家说英语!~
6 c.show(new Russia()); //我们国家说俄语!~
7 c.show(new France()); //我们国家说法语!~
8 Console.ReadKey();
9 }
10 class China : Person //中国
11 {
12 public void Speak() //注意增加了override,表示方法重写
13 {
14 Console.WriteLine("我们国家说中国话!~");
15 }
16 }
17 class America : Person //美国
18 {
19 public void Speak() //注意增加了override,表示方法重写
20 {
21 Console.WriteLine("我们国家说英语!~");
22 }
23 }
24 class Russia : Person//俄罗斯
25 {
26 public void Speak() //注意增加了override,表示方法重写
27 {
28 Console.WriteLine("我们国家说俄语!~");
29 }
30 }
31 class France : Person //法国
32 {
33 public void Speak()
34 {
35 Console.WriteLine("我们国家说法语!~");
36 }
37 }
38 class Country
39 {
40 public void show(Person person)
41 {
42 person.Speak();
43 }
44 }
45 interface Person
46 {
47 void Speak();
48 }
开放闭关原则:
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。