(原創) 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++
/* 2
(C) OOMusou 2007 http://oomusou.cnblogs.com3

4
Filename : DoorInheritance.cpp5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++6
Description : Demo how to use abstract class7
Release : 05/07/2007 1.08
*/9

10
#include <iostream>11
#include <vector>12
#include <algorithm>13
#include <functional>14

15
using namespace std;16

17
class Door {18
public: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

28
class HorizontalDoor : public Door {29
};30

31
class VerticalDoor : public Door {32
public:33
void open() const {34
cout << "open vertically" << endl;35
}36
37
void close() const {38
cout << "close vertically" << endl;39
}40
};41

42
class DoorController {43
protected:44
vector<Door*> _doorVec;45
46
public: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

57
int main() {58
DoorController dc;59
dc.addDoor(HorizontalDoor());60
dc.addDoor(VerticalDoor());61
dc.openDoor();62
}
C#
/* 2
(C) OOMusou 2007 http://oomusou.cnblogs.com3

4
Filename : DoorInheritance.cs5
Compiler : Visual Studio 2005 / C# 2.06
Description : Demo how to use abstract class7
Release : 05/07/2007 1.08
*/9
using System;10
using System.Collections.Generic;11

12
class 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

22
class HorizontalDoor : Door {23
}24

25
class 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

35
class 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

49
class 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++
/* 2
(C) OOMusou 2007 http://oomusou.cnblogs.com3

4
Filename : DoorInheritance.cpp5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++6
Description : Demo how to use interface7
Release : 05/07/2007 1.08
*/9

10
#include <iostream>11
#include <vector>12
#include <algorithm>13
#include <functional>14

15
using namespace std;16

17
class Door {18
public: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

28
class HorizontalDoor : public Door {29
};30

31
class VerticalDoor : public Door {32
public:33
void open() const {34
cout << "open vertically" << endl;35
}36
37
void close() const {38
cout << "close vertically" << endl;39
}40
};41

42
class IAlarm {43
public:44
virtual void alert() const = 0;45
};46

47
class Alarm : public IAlarm {48
public:49
void alert() const {50
cout << "ring,ring,ring" << endl;51
}52
};53

54
class AlarmDoor : public Door {55
protected:56
IAlarm* _alarm;57
58
public:59
AlarmDoor() {60
_alarm = new Alarm;61
}62
63
~AlarmDoor() {64
delete _alarm;65
}66

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

73
class DoorController {74
protected:75
vector<Door*> _doorVec;76
77
public: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

87
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#
/* 2
(C) OOMusou 2007 http://oomusou.cnblogs.com3

4
Filename : DoorInheritance.cs5
Compiler : Visual Studio 2005 / C# 2.06
Description : Demo how to use interface7
Release : 05/07/2007 1.08
*/9

10
using System;11
using System.Collections.Generic;12

13
abstract 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

23
class HorizontalDoor : Door {24
}25

26
class 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

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

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

46
class 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

58
class 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

72
class 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)


浙公网安备 33010602011771号