类,抽象类与接口之我所见!

  在学习过程中,抽象类与接口二者的关系已被高手阐述了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 ....

如果我们定义的类只有具体的人,那以上代码基本可以实现功能,我们也没必要非要用接口,但是.... ,如果有一天我们还要增加其他的类别,比如说:
 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 

    比较两段代码,有没有发现其中有共同的部分,其实两个种类有相同的方法成员,(交谈和移动),我们可以把它提取出来做为接口,让所有具体有该行为的类继承该接口.

     可是,慢着,如果我不提取出来,保持现在这个样子不是很好吗? 现在看起来是没有任何问题,但是如果某一天,系统要求中国人,美国人,鸡,羊都要增加"吃饭" 这个方法呢? 以后还要增加 "喝水" ,"高兴的表情".... 诸如此类,,现在只有两个抽象类还不算太坏,如果还增加其他种类,比如说:我想从动物种类中单独出来 "家畜类","哺乳动物类","鸟类".... 在这种情况下还是用这种单一的抽象类继承是非常棘手的. 这时我们可以将所有类的普遍行为归为一个接口,而将每个种类特有的行为和属性归类到一个抽象类中,这样既保持了具体类既有它的特有行为,也有共同的行为 ,具体时候之后大概会是酱紫:
 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 


  经过上面的改造,定义的麻雀和中国人既有自己的特殊方法,也有普遍方法,(有人说把接口定义改成抽象方法,虽然也可以,但是类继承是单一继承的,多继承好象不可以),当然,你也可以扩展你的接口,例如说你可以将 鸟类,猫科动物类共同的行为提取出来,做为一个新的接口.定义一个新类,要他包含接口中定义的所有方法,那么就继承该接口并实现它.

 抽象类和接口在结构比较单一的类集合中区别不是太大,但如果系统复杂,类关系多样,利用二者,相互结合使用,会大大降低系统耦合度,也让整个系统功能更容易扩展,其内部结构也一目了然.
 
    个人认为,抽象类注重小范围的,具有某些共性成员的成员描述,而接口,是对某些成员具有的共同行为的形态描述.它是不限范围的.

原文地址:https://www.cnblogs.com/kingcat/p/758323.html