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
4
Filename : DoorInheritance.cpp
5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6
Description : Demo how to use abstract class
7
Release : 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
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#
1
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : DoorInheritance.cs
5
Compiler : Visual Studio 2005 / C# 2.0
6
Description : Demo how to use abstract class
7
Release : 05/07/2007 1.0
8
*/
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++
1
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : DoorInheritance.cpp
5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6
Description : Demo how to use interface
7
Release : 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
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#
1
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : DoorInheritance.cs
5
Compiler : Visual Studio 2005 / C# 2.0
6
Description : Demo how to use interface
7
Release : 05/07/2007 1.0
8
*/
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)