(原創) 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.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#
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 vertically
假如日後需求改變,需要一個會警報的門AlarmDoor,由於廠商本身沒有生產警報器,所以勢必外包,這時他將規格定義成IAlarm interface,只要能生產出這個規格的Alarm,就可以透過『組合』的方式產生出會警報的門,在此範例我們使用『組合』技術。
UML(以下這個圖我畫錯了,_alarm應該在AlarmDoor內,感謝frank28_nfls的指正,C++與C#的code是對的)
C++
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#
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 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)