(原創) interface和abstract class有何不同? (C/C++) (.NET) (C#)

原文地址为: (原創) interface和abstract class有何不同? (C/C++) (.NET) (C#)

Abstract
這兩個的確非常的像,主要都是為了實踐『多型』,但實際的用途並不一樣。

Introduction
interface和abstract class在語言層次的差異,我就不再贅述,本文主要是放在何時該使用interface?何時該使用abstract class?

interface用在當一個物件須和其他物件共同合作時,為了確保其他物件有我想要的method,所以定下interface要該物件遵守,在Design Pattern到處可以看到這種應用,如strategy,bridge,prototype...。

而abstract class是用在整個繼承體系的最上層,用來定義出整個繼承體系該有哪些method,子類別可以對這些method加以override,或維持和abstract class相同的功能。Design Pattern中的template method,factory method...等就是用這種手法。

或者更明白的說,我們知道在OO主要有兩種技術:繼承(Inheritance)和組合(Composition),而abstract class就是用在使用繼承技術時,而interface則是用在使用組合技術時。

使用繼承技術時,我們會將所有method由abstract class去宣告,然後由各子類別去override,若不得已某些class有自己的特殊method,則由該class自行宣告。

一旦使用組合時時,就牽涉到一個問題,你如何確保被你組合的物件有某個method呢?當你使用繼承時,因為所有的method都會被繼承,這不是問題,但組合就不一樣了,所以你必須建立一個interface,強迫要被你組合的物件,需實做這個interface,這樣當你要使用該物件時,才能確保有某個method可以呼叫。

Sample Code
以Door為例,Door是一個泛稱,適合當abstract class,定義出open()和close(),由於一般們都是水平左右開,所以可以將左右開的功能放在abstract class,今天有一個垂直上下開的門VerticalDoor,門是水平開的,明顯和abstract class不一樣,所以使用了override的方式去改寫,在此範例我們使用了『繼承』的技術。

UML



C++

 1 24a924a57ba6b3f2b51fc9edb7ea4186.jpe 9310e85a14af99de4811ff4c77f1f911.jpe /**/ /* 
 2(C) OOMusou 2007 http://oomusou.cnblogs.com
 3
 4Filename    : DoorInheritance.cpp
 5Compiler    : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
 6Description : Demo how to use abstract class
 7Release     : 05/07/2007 1.0
 8*/

 9
10 #include  < iostream >
11 #include  < vector >
12 #include  < algorithm >
13 #include  < functional >
14
15 using   namespace  std;
16
17 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  Door  {
18public:
19715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  virtual void open() const {
20    cout << "open horizontally" << endl;
21  }

22  
23715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  virtual void close() const {
24    cout << "close horizontally" << endl;
25  }

26}
;
27
28 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  HorizontalDoor :  public  Door  {
29}
;
30
31 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  VerticalDoor :  public  Door  {
32public:
33715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void open() const {
34    cout << "open vertically" << endl;
35  }

36  
37715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void close() const {
38    cout << "close vertically" << endl;
39  }

40}
;
41
42 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  DoorController  {
43protected:
44  vector<Door*> _doorVec;
45  
46public:
47715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void addDoor(Door& aDoor) {
48    _doorVec.push_back(&aDoor);
49  }

50  
51715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void openDoor() const {
52    for_each(_doorVec.begin(), _doorVec.end(), mem_fun(&Door::open));
53  }

54}
;
55
56
57 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe int  main()  {
58  DoorController dc;
59  dc.addDoor(HorizontalDoor());
60  dc.addDoor(VerticalDoor());
61  dc.openDoor();
62}


C#

 1 24a924a57ba6b3f2b51fc9edb7ea4186.jpe 9310e85a14af99de4811ff4c77f1f911.jpe /**/ /* 
 2(C) OOMusou 2007 http://oomusou.cnblogs.com
 3
 4Filename    : DoorInheritance.cs
 5Compiler    : Visual Studio 2005 / C# 2.0
 6Description : Demo how to use abstract class
 7Release     : 05/07/2007 1.0
 8*/

 9 using  System;
10 using  System.Collections.Generic;
11
12 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  Door  {
13715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public virtual void open() {
14    Console.WriteLine("open horizontally");
15  }

16  
17715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public virtual void close() {
18    Console.WriteLine("close horizontally");
19  }

20}

21
22 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  HorizontalDoor : Door  {
23}

24
25 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  VerticalDoor : Door  {
26715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public override void open() {
27    Console.WriteLine("open vertically");
28  }

29  
30715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public override void close() {
31    Console.WriteLine("close vertically");
32  }

33}

34
35 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  DoorController  {
36  protected List<Door> _doorList = new List<Door>();
37  
38715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public void addDoor(Door aDoor) {
39    _doorList.Add(aDoor);
40  }

41  
42715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public void openDoor() {
43715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe    foreach(Door iter in _doorList) {
44      iter.open();
45    }

46  }

47}

48
49 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  main  {
50715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public static void Main() {
51    DoorController dc = new DoorController();
52    dc.addDoor(new HorizontalDoor());
53    dc.addDoor(new VerticalDoor());
54    dc.openDoor();
55  }

56}


執行結果

open horizontally
open vertically

假如日後需求改變,需要一個會警報的門AlarmDoor,由於廠商本身沒有生產警報器,所以勢必外包,這時他將規格定義成IAlarm interface,只要能生產出這個規格的Alarm,就可以透過『組合』的方式產生出會警報的門,在此範例我們使用『組合』技術。

UML(以下這個圖我畫錯了,_alarm應該在AlarmDoor內,感謝frank28_nfls的指正,C++與C#的code是對的)




C++

 1 24a924a57ba6b3f2b51fc9edb7ea4186.jpe 9310e85a14af99de4811ff4c77f1f911.jpe /**/ /* 
 2(C) OOMusou 2007 http://oomusou.cnblogs.com
 3
 4Filename    : DoorInheritance.cpp
 5Compiler    : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
 6Description : Demo how to use interface
 7Release     : 05/07/2007 1.0
 8*/

 9
10 #include  < iostream >
11 #include  < vector >
12 #include  < algorithm >
13 #include  < functional >
14
15 using   namespace  std;
16
17 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  Door  {
18public:
19715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  virtual void open() const {
20    cout << "open horizontally" << endl;
21  }

22  
23715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  virtual void close() const {
24    cout << "close horizontally" << endl;
25  }

26}
;
27
28 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  HorizontalDoor :  public  Door  {
29}
;
30
31 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  VerticalDoor :  public  Door  {
32public:
33715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void open() const {
34    cout << "open vertically" << endl;
35  }

36  
37715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void close() const {
38    cout << "close vertically" << endl;
39  }

40}
;
41
42 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  IAlarm  {
43public:
44  virtual void alert() const = 0;
45}
;
46
47 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  Alarm :  public  IAlarm  {
48public:
49715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void alert() const {
50    cout << "ring,ring,ring" << endl;
51  }

52}
;
53
54 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  AlarmDoor :  public  Door  {
55protected:
56  IAlarm* _alarm;
57  
58public:
59715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  AlarmDoor() {
60    _alarm = new Alarm;
61  }

62  
63715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  ~AlarmDoor() {
64    delete _alarm;
65  }

66
67public:
68715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void alert() {
69    _alarm->alert();
70  }

71}
;
72
73 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  DoorController  {
74protected:
75  vector<Door*> _doorVec;
76  
77public:
78715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void addDoor(Door& aDoor) {
79    _doorVec.push_back(&aDoor);
80  }

81  
82715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  void openDoor() {
83    for_each(_doorVec.begin(), _doorVec.end(), mem_fun(&Door::open));
84  }

85}
;
86
87 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe int  main()  {
88  DoorController dc;
89  dc.addDoor(HorizontalDoor());
90  dc.addDoor(VerticalDoor());
91  dc.addDoor(AlarmDoor());
92  dc.openDoor();
93  
94  Door& door = AlarmDoor();
95  dynamic_cast<AlarmDoor&>(door).alert();
96}


C#

 1 24a924a57ba6b3f2b51fc9edb7ea4186.jpe 9310e85a14af99de4811ff4c77f1f911.jpe /**/ /* 
 2(C) OOMusou 2007 http://oomusou.cnblogs.com
 3
 4Filename    : DoorInheritance.cs
 5Compiler    : Visual Studio 2005 / C# 2.0
 6Description : Demo how to use interface
 7Release     : 05/07/2007 1.0
 8*/

 9
10 using  System;
11 using  System.Collections.Generic;
12
13 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe abstract   class  Door  {
14715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public virtual void open() {
15    Console.WriteLine("open horizontally");
16  }

17  
18715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public virtual void close() {
19    Console.WriteLine("close horizontally");
20  }

21}

22
23 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  HorizontalDoor : Door  {
24}

25
26 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  VerticalDoor : Door  {
27715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  override public void open() {
28    Console.WriteLine("open vertically");
29  }

30  
31715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  override public void close() {
32    Console.WriteLine("close vertically");
33  }

34}

35
36 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe interface  IAlarm  {
37  void alert();
38}

39
40 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  Alarm : IAlarm  {
41715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public void alert() {
42    Console.WriteLine("ring,ring,ring");
43  }

44}

45
46 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  AlarmDoor : Door  {
47  private IAlarm _alarm;
48  
49715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public AlarmDoor() {
50    _alarm = new Alarm();
51  }

52
53715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public void alert() {
54    _alarm.alert();
55  }

56}

57
58 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  DoorController  {
59  protected List<Door> _doorList = new List<Door>();
60
61715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public void addDoor(Door aDoor) {
62    _doorList.Add(aDoor);
63  }

64
65715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public void openDoor() {
66715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe    foreach (Door iter in _doorList) {
67      iter.open();
68    }

69  }

70}

71
72 24a924a57ba6b3f2b51fc9edb7ea4186.jpe9310e85a14af99de4811ff4c77f1f911.jpe class  main  {
73715f2d05503b99d41f3b6ba2cdccc84d.jpee083dfde5a91f50979fe8979b4012b9d.jpe  public static void Main() {
74    DoorController dc = new DoorController();
75    dc.addDoor(new HorizontalDoor());
76    dc.addDoor(new VerticalDoor());
77    dc.addDoor(new AlarmDoor());
78    dc.openDoor();
79  
80    Door door = new AlarmDoor();
81    ((AlarmDoor)(door)).alert();
82  }

83}


執行結果

open horizontally
open vertically
open horizontally
ring
, ring , ring


值得注意的是,我並沒有將Door()這個abstract class加上alert(),因為alert()並非所有門都有,所以不應該放在abstract class,另外abstract class也不該隨意更改,所以才會說,abstract class和interface在設計時非常重要,整個多型的技術都是靠interface和abstract class支撐,只要interface或abstract class一變,整個多型機制就瓦解了。

Conclusion
interface和abstract class的差異,重點是在用的地方完全不同,而非僅是語法上的小差異,若從語言差異的角度去看interface和abstract class,當然會搞的一頭霧水,若從OO和Design Pattern的角度去看,才能較容易分辨interface和abstract的異同。

See Also
(原創) 我對interface的理解 (初級) (C/C++) (Design Pattern)


转载请注明本文地址: (原創) interface和abstract class有何不同? (C/C++) (.NET) (C#)

猜你喜欢

转载自blog.csdn.net/w36680130/article/details/81385913
今日推荐