每一個可以努力的日子,都是一份厚禮。
該用 Abstract Class 還是 Interface?
在編寫程序的時候我常常陷入糾結,一個抽象對象,到底應該定義成 抽象類(Abstract Class) 還是 接口(Interface) 呢?二者具有很大的相似性,甚至可以相互替換,難以選擇。在 Stackoverflow 上這個問題被問了很多次,各種編程語言的都有。而在 PHP 官網文檔 Abstract Class 和 Interface 章節下面的評論里,人們也是爭論不休。為了弄明白這個問題,必須仔細對比一下二者的區別和使用場景。
一、Abstract Class 與 Interface 的構造
抽象類 Abstract Class
<?php abstract class A { abstract public function method1(); abstract public function method2(); public function method3() { //... code ... } } ?> |
接口 Interface
<?php interface B { public function method4(); public function method5(); } ?> |
可以看到,Abstract Class 中可以有抽象函數(method1,method2),也可以有具體的函數實現(method3),而 Interface 中只能定義函數而不能有實現(method4,method5)。顯然,如果一個抽象類中的函數全部是抽象函數,那麼這個抽象類就退化成了接口。不過要注意的是
- PHP 和 Java 一樣,一個 Class 只能繼承一個 Abstract Class,但可以實現多個 Interface。這即是所謂的單一繼承體系,也就是子類別只能繼承一個父類別;一個父類別則可以被多個子類別所繼承。據說在 Python 中是可以多重繼承的。
- 在 Abstract Class 中可以聲明屬性成員變量(attribute),而 Interface 不可以。
二、舉例
下面的例子可以幫助我們從另一個層面:Abstract Class 和 Interface 所反映出的設計理念,來分析一下二者的本質區別。
假設我們要定義一個關於“門”的 Class,門有“開”和“關”兩個動作。此時我們既可以用 Abstract Class 也可以用 Interface 來描述這個抽象概念——
抽象類 Abstract Class
<?php abstract class Door { abstract public function open(); abstract public function close(); } ?> |
接口 Interface
<?php interface Door { public function open(); public function close(); } ?> |
在 Abstract Class 中並沒有實現開門和關門方法,因為有的門可以用鑰匙打開,有的門用密碼打開,還有的門可以用指紋打開,這些具體方法就交給子類繼承去實現。在這樣看上去 Abstract Class 和 Interface 二者的使用沒有太大差別。
現在如果定義一種具有“報警”功能的門,Abstract Class 和 Interface 描述類似——
抽象類 Abstract Class
<?php abstract class Door { abstract public function open(); abstract public function close(); abstract public function alarm(); } class AlarmDoor extends Door { public function open() { //... } public function close() { //... } public function alarm() { //... } } ?> |
接口 Interface
<?php interface Door { public function open(); public function close(); public function alerm(); } class AlarmDoor implements Door { public function open() { //... } public function close() { //... } public function alarm() { //... } } ?> |
很明顯上面的做法是不對的。它在定義中把 Door 本身固有的開門、關門的行為方法和另外一個概念“報警器”的行為方法混在了一起。有的門可以報警,而有的門可能沒有報警功能;而會報警的門也可能多種多樣(鑰匙開門,刷卡開門等等)。我們必須將報警行為單獨定義到另一個對象中,那麼就有 3 種方式:
- 門和報警器都用 abstract class 方式定義;
- 門和報警器都用 interface 方式定義;
- 一個使用 abstract class 方式定義,另一個使用 interface 方式定義。
由於 PHP 中子類只可繼承自一個 Abstract Class,而且 Abstract Class 不支持多重繼承(abstract class cannot extend abstract class),所以第一種方式顯然不行。而第二種方式則沒有能夠正確的揭示我們的設計意圖,也就是沒有反映出 AlarmDoor 在概念本質上是 Door,同時它有具有報警功能。所以對於 Door 這個概念,我們應該採取 Abstract Class 方式來定義,AlarmDoor 繼承自 Door,而報警概念通過 Interface 方式定義。
<?php abstract class Door { abstract function open(); abstract function close(); } interface Alarm { function alarm(); } class AlarmDoor extends Door implements Alarm { public function open() { //... } public function close() { //... } public function alarm() { //... } } ?> |
這樣的實現基本上正確地反映了我們的設計意圖。接口是可以多重繼承的,子類也可以實現多個接口。這樣就使得我們可以繼續擴展門的功能,比如給門再裝個攝像頭……
三、結論
當我們使用 Abstract Class 的時候,我們應該定義一類對象的屬性,即描述它 是什麼。而 Interface 則類似於插件,側重於提供附加的能力,約定它 能做什麼。
參考鏈接:
該用 Abstract Class 還是 Interface?
深入理解abstract class和interface
這篇文章由lovelucy於2013-03-30 23:46發表在編程。你可以訂閱RSS 2.0 也可以發表評論或引用到你的網站。除特殊說明外文章均為本人原創,並遵從署名-非商業性使用-相同方式共享創作協議,轉載或使用請註明作者和來源,尊重知識分享。 |
批評不自由
則讚美無意義
good example but you have the abstract class 支持多重繼承。a abstract class can extend from another abstract.
http://stackoverflow.com/questions/7269641/php-abstract-class-extending-another-abstract-class
有幸拜讀博主的文章,受益匪淺。