(原創) 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/* 
 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
15using namespace std;
16
17class Door {
18public:
19  virtual void open() const {
20    cout << "open horizontally" << endl;
21  }

22  
23  virtual void close() const {
24    cout << "close horizontally" << endl;
25  }

26}
;
27
28class HorizontalDoor : public Door {
29}
;
30
31class VerticalDoor : public Door {
32public:
33  void open() const {
34    cout << "open vertically" << endl;
35  }

36  
37  void close() const {
38    cout << "close vertically" << endl;
39  }

40}
;
41
42class DoorController {
43protected:
44  vector<Door*> _doorVec;
45  
46public:
47  void addDoor(Door& aDoor) {
48    _doorVec.push_back(&aDoor);
49  }

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

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


C#

 1/* 
 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*/

 9using System;
10using System.Collections.Generic;
11
12class Door {
13  public virtual void open() {
14    Console.WriteLine("open horizontally");
15  }

16  
17  public virtual void close() {
18    Console.WriteLine("close horizontally");
19  }

20}

21
22class HorizontalDoor : Door {
23}

24
25class VerticalDoor : Door {
26  public override void open() {
27    Console.WriteLine("open vertically");
28  }

29  
30  public override void close() {
31    Console.WriteLine("close vertically");
32  }

33}

34
35class DoorController {
36  protected List<Door> _doorList = new List<Door>();
37  
38  public void addDoor(Door aDoor) {
39    _doorList.Add(aDoor);
40  }

41  
42  public void openDoor() {
43    foreach(Door iter in _doorList) {
44      iter.open();
45    }

46  }

47}

48
49class main {
50  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/* 
 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
15using namespace std;
16
17class Door {
18public:
19  virtual void open() const {
20    cout << "open horizontally" << endl;
21  }

22  
23  virtual void close() const {
24    cout << "close horizontally" << endl;
25  }

26}
;
27
28class HorizontalDoor : public Door {
29}
;
30
31class VerticalDoor : public Door {
32public:
33  void open() const {
34    cout << "open vertically" << endl;
35  }

36  
37  void close() const {
38    cout << "close vertically" << endl;
39  }

40}
;
41
42class IAlarm {
43public:
44  virtual void alert() const = 0;
45}
;
46
47class Alarm : public IAlarm {
48public:
49  void alert() const {
50    cout << "ring,ring,ring" << endl;
51  }

52}
;
53
54class AlarmDoor : public Door {
55protected:
56  IAlarm* _alarm;
57  
58public:
59  AlarmDoor() {
60    _alarm = new Alarm;
61  }

62  
63  ~AlarmDoor() {
64    delete _alarm;
65  }

66
67public:
68  void alert() {
69    _alarm->alert();
70  }

71}
;
72
73class DoorController {
74protected:
75  vector<Door*> _doorVec;
76  
77public:
78  void addDoor(Door& aDoor) {
79    _doorVec.push_back(&aDoor);
80  }

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

85}
;
86
87int 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/* 
 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
10using System;
11using System.Collections.Generic;
12
13abstract class Door {
14  public virtual void open() {
15    Console.WriteLine("open horizontally");
16  }

17  
18  public virtual void close() {
19    Console.WriteLine("close horizontally");
20  }

21}

22
23class HorizontalDoor : Door {
24}

25
26class VerticalDoor : Door {
27  override public void open() {
28    Console.WriteLine("open vertically");
29  }

30  
31  override public void close() {
32    Console.WriteLine("close vertically");
33  }

34}

35
36interface IAlarm {
37  void alert();
38}

39
40class Alarm : IAlarm {
41  public void alert() {
42    Console.WriteLine("ring,ring,ring");
43  }

44}

45
46class AlarmDoor : Door {
47  private IAlarm _alarm;
48  
49  public AlarmDoor() {
50    _alarm = new Alarm();
51  }

52
53  public void alert() {
54    _alarm.alert();
55  }

56}

57
58class DoorController {
59  protected List<Door> _doorList = new List<Door>();
60
61  public void addDoor(Door aDoor) {
62    _doorList.Add(aDoor);
63  }

64
65  public void openDoor() {
66    foreach (Door iter in _doorList) {
67      iter.open();
68    }

69  }

70}

71
72class main {
73  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)

posted on 2007-05-07 21:45  真 OO无双  阅读(58402)  评论(2编辑  收藏  举报

导航