类,抽象类与接口之我所见!
在学习过程中,抽象类与接口二者的关系已被高手阐述了N遍,但是,仅仅罗列出他们之间的区别是不能够表述清晰的,我看了Anytao和KeithDa两位高手写的文章,他们说的都非常好,让我受益非浅,结合二位前辈的经验和我在项目中的体会,斗胆也谈谈他们的关系和取舍!
定义:
接口:接口是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值。接口方法不能包含任何实现;
抽象类:抽象类提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法.
应用与取舍:
二者的使用要看具体情况,有可能只有其中一种,也可能全部使用效果才好.下面做一个我自己认为能说明问题的例子:
如果我们定义的类只有具体的人,那以上代码基本可以实现功能,我们也没必要非要用接口,但是.... ,如果有一天我们还要增加其他的类别,比如说:
比较两段代码,有没有发现其中有共同的部分,其实两个种类有相同的方法成员,(交谈和移动),我们可以把它提取出来做为接口,让所有具体有该行为的类继承该接口.
可是,慢着,如果我不提取出来,保持现在这个样子不是很好吗? 现在看起来是没有任何问题,但是如果某一天,系统要求中国人,美国人,鸡,羊都要增加"吃饭" 这个方法呢? 以后还要增加 "喝水" ,"高兴的表情".... 诸如此类,,现在只有两个抽象类还不算太坏,如果还增加其他种类,比如说:我想从动物种类中单独出来 "家畜类","哺乳动物类","鸟类".... 在这种情况下还是用这种单一的抽象类继承是非常棘手的. 这时我们可以将所有类的普遍行为归为一个接口,而将每个种类特有的行为和属性归类到一个抽象类中,这样既保持了具体类既有它的特有行为,也有共同的行为 ,具体时候之后大概会是酱紫:
定义:
接口:接口是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值。接口方法不能包含任何实现;
抽象类:抽象类提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法.
应用与取舍:
二者的使用要看具体情况,有可能只有其中一种,也可能全部使用效果才好.下面做一个我自己认为能说明问题的例子:
1 // 定义人的抽象类
2 abstract class people
3 {
4 // 定义成员
5 protected string _name;
6
7 // 定义姓名属性
8 public abstract string name
9 {
10 get:
11 set:
12 }
13
14 // 定义交谈方法
15 public abstract void talk()
16
17 // 定义移动方法
18 public abstract void move()
19 }
20
21
22 /////////////////////////////////////////////
23
24 // 定义中国人
25 abstract class Chinaman:people
26 {
27 // 构造函数
28 public Chinaman(string strname)
29 {
30 _name = strname
31 }
32
33 // 实现姓名属性成员
34 public override string name
35 {
36 get
37 {
38 return _name;
39 }
40 set
41 {
42 _name = value;
43 }
44 }
45
46 // 实现交谈方法
47 public override void talk()
48 {
49 Response.write("我说的是汉语!");
50 }
51
52 // 实现移动方法
53 public override void move()
54 {
55 Response.write("我步行移动!");
56 }
57 }
58
59
60 // 定义美国人
61 abstract class American:people
62 {
63 // 构造函数
64 public American(string strname)
65 {
66 _name = strname
67 }
68
69 // 实现姓名属性成员
70 public override string name
71 {
72 get
73 {
74 return _name;
75 }
76 set
77 {
78 _name = value;
79 }
80 }
81
82 // 实现交谈方法
83 public override void talk()
84 {
85 Response.write("我说的是英语!");
86 }
87
88 // 实现移动方法
89 public override void move()
90 {
91 Response.write("我开车移动!");
92 }
93 }
94
95 ....
2 abstract class people
3 {
4 // 定义成员
5 protected string _name;
6
7 // 定义姓名属性
8 public abstract string name
9 {
10 get:
11 set:
12 }
13
14 // 定义交谈方法
15 public abstract void talk()
16
17 // 定义移动方法
18 public abstract void move()
19 }
20
21
22 /////////////////////////////////////////////
23
24 // 定义中国人
25 abstract class Chinaman:people
26 {
27 // 构造函数
28 public Chinaman(string strname)
29 {
30 _name = strname
31 }
32
33 // 实现姓名属性成员
34 public override string name
35 {
36 get
37 {
38 return _name;
39 }
40 set
41 {
42 _name = value;
43 }
44 }
45
46 // 实现交谈方法
47 public override void talk()
48 {
49 Response.write("我说的是汉语!");
50 }
51
52 // 实现移动方法
53 public override void move()
54 {
55 Response.write("我步行移动!");
56 }
57 }
58
59
60 // 定义美国人
61 abstract class American:people
62 {
63 // 构造函数
64 public American(string strname)
65 {
66 _name = strname
67 }
68
69 // 实现姓名属性成员
70 public override string name
71 {
72 get
73 {
74 return _name;
75 }
76 set
77 {
78 _name = value;
79 }
80 }
81
82 // 实现交谈方法
83 public override void talk()
84 {
85 Response.write("我说的是英语!");
86 }
87
88 // 实现移动方法
89 public override void move()
90 {
91 Response.write("我开车移动!");
92 }
93 }
94
95 ....
如果我们定义的类只有具体的人,那以上代码基本可以实现功能,我们也没必要非要用接口,但是.... ,如果有一天我们还要增加其他的类别,比如说:
1 // 定义动物的抽象类
2 abstract class animal
3 {
4 // 定义成员
5 protected string _kind;
6
7 // 定义类型属性
8 public abstract string kind
9 {
10 get:
11 set:
12 }
13
14 // 定义交谈方法
15 public abstract void talk()
16
17 // 定义移动方法
18 public abstract void move()
19 }
20
21
22 /////////////////////////////////////////////
23
24 // 定义羊
25 abstract class sheep:animal
26 {
27 // 构造函数
28 public sheep(string strkind)
29 {
30 _kind = strkind
31 }
32
33 // 实现类型属性成员
34 public override string kind
35 {
36 get
37 {
38 return _kind;
39 }
40 set
41 {
42 _kind = value;
43 }
44 }
45
46 // 实现交谈方法
47 public override void talk()
48 {
49 Response.write("咩咩的叫!");
50 }
51
52 // 实现移动方法
53 public override void move()
54 {
55 Response.write("四蹄行走!");
56 }
57 }
58
59
60 // 定义鸡
61 abstract class chicken:animal
62 {
63 // 构造函数
64 public chicken(string strkind)
65 {
66 _kind = strname
67 }
68
69 // 实现类型属性成员
70 public override string kind
71 {
72 get
73 {
74 return _kind;
75 }
76 set
77 {
78 _kind = value;
79 }
80 }
81
82 // 实现交谈方法
83 public override void talk()
84 {
85 Response.write("喔喔的叫!");
86 }
87
88 // 实现移动方法
89 public override void move()
90 {
91 Response.write("两爪行走!");
92 }
93 }
94
95
96
2 abstract class animal
3 {
4 // 定义成员
5 protected string _kind;
6
7 // 定义类型属性
8 public abstract string kind
9 {
10 get:
11 set:
12 }
13
14 // 定义交谈方法
15 public abstract void talk()
16
17 // 定义移动方法
18 public abstract void move()
19 }
20
21
22 /////////////////////////////////////////////
23
24 // 定义羊
25 abstract class sheep:animal
26 {
27 // 构造函数
28 public sheep(string strkind)
29 {
30 _kind = strkind
31 }
32
33 // 实现类型属性成员
34 public override string kind
35 {
36 get
37 {
38 return _kind;
39 }
40 set
41 {
42 _kind = value;
43 }
44 }
45
46 // 实现交谈方法
47 public override void talk()
48 {
49 Response.write("咩咩的叫!");
50 }
51
52 // 实现移动方法
53 public override void move()
54 {
55 Response.write("四蹄行走!");
56 }
57 }
58
59
60 // 定义鸡
61 abstract class chicken:animal
62 {
63 // 构造函数
64 public chicken(string strkind)
65 {
66 _kind = strname
67 }
68
69 // 实现类型属性成员
70 public override string kind
71 {
72 get
73 {
74 return _kind;
75 }
76 set
77 {
78 _kind = value;
79 }
80 }
81
82 // 实现交谈方法
83 public override void talk()
84 {
85 Response.write("喔喔的叫!");
86 }
87
88 // 实现移动方法
89 public override void move()
90 {
91 Response.write("两爪行走!");
92 }
93 }
94
95
96
比较两段代码,有没有发现其中有共同的部分,其实两个种类有相同的方法成员,(交谈和移动),我们可以把它提取出来做为接口,让所有具体有该行为的类继承该接口.
可是,慢着,如果我不提取出来,保持现在这个样子不是很好吗? 现在看起来是没有任何问题,但是如果某一天,系统要求中国人,美国人,鸡,羊都要增加"吃饭" 这个方法呢? 以后还要增加 "喝水" ,"高兴的表情".... 诸如此类,,现在只有两个抽象类还不算太坏,如果还增加其他种类,比如说:我想从动物种类中单独出来 "家畜类","哺乳动物类","鸟类".... 在这种情况下还是用这种单一的抽象类继承是非常棘手的. 这时我们可以将所有类的普遍行为归为一个接口,而将每个种类特有的行为和属性归类到一个抽象类中,这样既保持了具体类既有它的特有行为,也有共同的行为 ,具体时候之后大概会是酱紫:
1 // 人的抽象类
2 public abstract class people
3 {
4 // 人类特有的行为
5 public abstract void 劳动()
6 }
7
8 // 鸟类的抽象类
9 public abstract class Aves
10 {
11 // 鸟类动物特有的行为
12 public abstract void 飞翔()
13 }
14
15 // 公共接口
16 public interface IAction
17 {
18 // 普遍的行为
19 public void move();
20 public void talk();
21 }
22
23 /////////////////////////////////////////
24
25 // 定义中国人
26 public class Chinaman:people,IAction
27 {
28 // 实现抽象类和接口方法
29 }
30
31
32 // 定义麻雀
33 public class spadger:Aves,IAction
34 {
35 // 实现抽象类和接口方法
36 }
37
2 public abstract class people
3 {
4 // 人类特有的行为
5 public abstract void 劳动()
6 }
7
8 // 鸟类的抽象类
9 public abstract class Aves
10 {
11 // 鸟类动物特有的行为
12 public abstract void 飞翔()
13 }
14
15 // 公共接口
16 public interface IAction
17 {
18 // 普遍的行为
19 public void move();
20 public void talk();
21 }
22
23 /////////////////////////////////////////
24
25 // 定义中国人
26 public class Chinaman:people,IAction
27 {
28 // 实现抽象类和接口方法
29 }
30
31
32 // 定义麻雀
33 public class spadger:Aves,IAction
34 {
35 // 实现抽象类和接口方法
36 }
37
经过上面的改造,定义的麻雀和中国人既有自己的特殊方法,也有普遍方法,(有人说把接口定义改成抽象方法,虽然也可以,但是类继承是单一继承的,多继承好象不可以),当然,你也可以扩展你的接口,例如说你可以将 鸟类,猫科动物类共同的行为提取出来,做为一个新的接口.定义一个新类,要他包含接口中定义的所有方法,那么就继承该接口并实现它.
抽象类和接口在结构比较单一的类集合中区别不是太大,但如果系统复杂,类关系多样,利用二者,相互结合使用,会大大降低系统耦合度,也让整个系统功能更容易扩展,其内部结构也一目了然.
个人认为,抽象类注重小范围的,具有某些共性成员的成员描述,而接口,是对某些成员具有的共同行为的形态描述.它是不限范围的.