[英]Interface or an Abstract Class: which one to use?
請解釋我何時應該使用PHP interface
以及什么時候應該使用abstract class
?
如何將abstract class
更改為interface
?
如果要強制在系統中工作的開發人員(包括您自己)在他們將要構建的類上實現一定數量的方法,請使用接口。
如果要強制在系統中工作的開發人員(包括您自己)實現一定數量的方法, 並且希望提供一些有助於他們開發子類的基本方法,請使用抽象類。
要記住的另一件事是客戶端類只能擴展一個抽象類,而它們可以實現多個接口。 因此,如果您在抽象類中定義行為合同,則意味着每個子類可能只符合單個合同。 有時這是一件好事,當你想強迫你的用戶程序員沿着特定的路徑行進時。 其他時候會很糟糕。 想象一下,如果PHP的Countable和Iterator接口是抽象類而不是接口。
Abstract Class
和Interface
之間的區別:
抽象類
抽象類可以提供一些功能 , 剩下的就是派生類 。
派生類可以覆蓋或不覆蓋基類中定義的具體函數。
從抽象類擴展的子類在邏輯上應該是相關的。
接口
接口不能包含任何功能 。 它僅包含的方法定義。
派生類必須為接口中定義的所有方法提供代碼 。
完全不同的和不相關的類可以使用接口在邏輯上組合在一起。
為什么要使用抽象類? 以下是一個簡單的例子。 可以說我們有以下代碼:
<?php
class Fruit {
private $color;
public function eat() {
// chew
}
public function setColor($c) {
$this->color = $c;
}
}
class Apple extends Fruit {
public function eat() {
// chew until core
}
}
class Orange extends Fruit {
public function eat() {
// peeling
// chew
}
}
現在我給你一個蘋果然后吃它。 嘗起來怎么樣? 它的味道像蘋果。
<?php
$apple = new Apple();
$apple->eat();
// Now I give you a fruit.
$fruit = new Fruit();
$fruit->eat();
這味道是什么樣的? 嗯,它沒有多大意義,所以你不應該這樣做。 這是通過使Fruit類抽象以及其中的eat方法來實現的。
<?php
abstract class Fruit {
private $color;
abstract public function eat(){}
public function setColor($c) {
$this->color = $c;
}
}
?>
抽象類就像一個接口,但您可以在抽象類中定義方法,而在接口中它們都是抽象的。 抽象類可以同時具有空和工作/具體方法。 在接口中,那里定義的函數不能有一個體。 在抽象類中,他們可以。
一個現實世界的例子:
<?php
abstract class person {
public $LastName;
public $FirstName;
public $BirthDate;
abstract protected function write_info();
}
final class employee extends person{
public $EmployeeNumber;
public $DateHired;
public function write_info(){
//sql codes here
echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>";
}
}
final class student extends person{
public $StudentNumber;
public $CourseName;
public function write_info(){
//sql codes here
echo "Writing ". $this->LastName . "'s info to student dbase table <br>";
}
}
///----------
$personA = new employee;
$personB = new student;
$personA->FirstName="Joe";
$personA->LastName="Sbody";
$personB->FirstName="Ben";
$personB->LastName="Dover";
$personA->write_info();
// Writing Sbody's info to emloyee dbase table
$personB->write_info();
// Writing Dover's info to student dbase table
最佳實踐是使用接口來指定合同,並使用抽象類作為其一個實現。 該抽象類可以填充很多樣板文件,因此您可以通過覆蓋您需要或想要的內容來創建實現,而不必強制您使用特定的實現。
只是把它放到混合中,但正如Cletus提到的將接口與抽象類結合使用時,我經常使用界面來闡明我的設計思維。
例如:
<?php
class parser implements parserDecoratorPattern {
//...
}
這樣,任何閱讀我的代碼的人(以及誰知道Decorator模式是什么)都會馬上知道a)我如何構建我的解析器和b)能夠看到用於實現裝飾器模式的方法。
此外,我可能不在這里不是Java / C ++ / etc程序員,但數據類型可以在這里發揮作用。 您的對象屬於某種類型,當您以類型方式傳遞它們時會以編程方式進行操作。 將可契約項移動到接口中只會指示方法返回的類型,而不是實現它的類的基類型。
現在已經很晚了,我想不出更好的psudo代碼示例,但是這里有:
<?php
interface TelevisionControls {};
class Remote implements TelevisionControls {};
class Spouse implements TelevisionControls {};
Spouse spouse = new Spouse();
Remote remote = new Remote();
isSameType = (bool)(remote == spouse)
主要區別在於抽象類可以包含默認實現,而接口則不能。
接口是行為契約,沒有任何實現。
另外,我想在此添加,因為任何其他OO語言都有某種接口和抽象,這並不意味着它們具有與PHP相同的含義和目的。 抽象/接口的使用略有不同,而PHP中的接口實際上沒有真正的功能。 它們僅用於語義和與方案相關的原因。 關鍵是要使項目盡可能靈活,可擴展且安全,以便將來擴展,無論開發人員以后是否有完全不同的使用計划。
如果您的英語不是本地的,您可能會查找抽象和接口實際上是什么。 並尋找同義詞。
這可能會幫助你作為一個比喻:
接口
比方說,你用草莓烤一種新蛋糕,然后你制作了描述成分和步驟的食譜。 只有你知道為什么它的味道如此之好,你的客人喜歡它。 然后你決定發布你的食譜,以便其他人也可以嘗試那個蛋糕。
這里的要點是
- 使它正確
- 要小心
- 防止可能變壞的東西(如太多草莓或其他東西)
- 讓試用它的人容易
- 告訴你要做多久(如攪拌)
- 告訴你可以做但不必做的事情
這正是描述接口的原因。 它是一個指南,一組觀察食譜內容的指令。 就像你在PHP中創建一個項目一樣,你想在GitHub或你的配偶或其他什么提供代碼。 界面是人們可以做的,不應該做的。 持有它的規則 - 如果你不遵守它,整個結構將被打破。
ABSTRACTION
繼續這個比喻在這里...想象一下,你這次是客人吃那塊蛋糕。 然后你現在正在嘗試使用食譜的蛋糕。 但是您想要添加新成分或更改/跳過配方中描述的步驟。 接下來會發生什么? 計划該蛋糕的不同版本。 這次是用黑漿果而不是草莓漿和更多的香草奶油......美味。
這是你可以考慮的原始蛋糕的擴展。 你基本上通過創建一個新的配方來對它進行抽象,因為它不同。 它有一些新的步驟和其他成分。 然而,黑莓版本有一些你從原版中接過的部分 - 這些是每種蛋糕必須具備的基本步驟。 像牛奶一樣的成分 - 這就是每個派生階級所擁有的。
現在你想要交換成分和步驟,這些必須在那個蛋糕的新版本中定義。 這些是必須為新蛋糕定義的抽象方法 ,因為蛋糕中應該有水果但是哪個? 所以你這次采取黑漿果。 完成。
你去了,你已經延長了蛋糕,按照界面和抽象的步驟和成分。
從哲學的角度來看:
抽象類表示“是一種”關系。 讓我說我有水果,我會有一個水果抽象類,分享共同的責任和共同的行為。
界面代表“應該做”的關系。 在我看來,一個界面(這是一個初級開發者的意見),應該通過一個動作命名,或者一個接近一個動作的東西(抱歉,找不到這個詞,我不是英語為母語的人)讓我們說IEatable。 你知道它可以吃,但你不知道你吃什么。
從編碼的角度來看:
如果您的對象具有重復的代碼,則表明它們具有共同的行為,這意味着您可能需要一個抽象類來重用代碼,這是您無法對接口執行的。
另一個區別是對象可以根據需要實現盡可能多的接口,但由於“鑽石問題”,您只能擁有一個抽象類(請查看此處了解原因! http://en.wikipedia.org/wiki/ Multiple_inheritance#The_diamond_problem )
我可能會忘記一些觀點,但我希望它可以澄清一些事情。
PS:Vivek Vermani的答案帶來了“是一個”/“應該做”,我不是故意偷他的答案,只是為了重復使用這些術語,因為我喜歡它們!
添加一些已經很好的答案:
抽象類讓你提供一定程度的實現,接口是純模板。 接口只能定義功能 ,它永遠不能實現它。
任何實現接口的類都會提交實現它定義的所有方法,或者必須將其聲明為abstract。
接口可以幫助管理像Java一樣不支持多重繼承的事實。 PHP類只能擴展單個父級。 但是,您可以使類承諾實現任意數量的接口。
type:對於它實現的每個接口,該類采用相應的類型。 因為任何類都可以實現接口(或更多接口),所以接口有效地連接其他不相關的類型。
類可以擴展超類並實現任意數量的接口:
class SubClass extends ParentClass implements Interface1, Interface2 { // ... }
請解釋我何時應該使用接口以及何時應該使用抽象類?
當您只需提供一個沒有實現的模板時,請使用接口,並且您希望確保實現該接口的任何類與實現它的任何其他類具有相同的方法(至少)。
如果要為其他對象(部分構建的類)創建基礎,請使用抽象類。 擴展抽象類的類將使用定義/實現的一些屬性或方法:
<?php
// interface
class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code.
// abstract class
class X extends Y { } // this is saying that "X" is going to complete the partial class "Y".
?>
如何將抽象類更改為界面?
這是一個簡化的案例/示例。 取出任何實施細節。 例如,從以下位置更改抽象類:
abstract class ClassToBuildUpon {
public function doSomething() {
echo 'Did something.';
}
}
至:
interface ClassToBuildUpon {
public function doSomething();
}
抽象類和接口之間的技術差異已經精確地列在其他答案中。 我想添加一個解釋來在類和接口之間進行選擇,同時為了面向對象的編程而編寫代碼。
類應表示實體,而接口應表示行為。
我們來舉個例子吧。 計算機監視器是一個實體,應該表示為一個類。
class Monitor{
private int monitorNo;
}
它旨在為您提供顯示界面,因此功能應由界面定義。
interface Display{
void display();
}
還有很多其他的事情要考慮,正如其他答案中所解釋的那樣,但這是大多數人在編碼時忽略的最基本的事情。
只是想添加一個例子,說明何時需要同時使用它們。 我目前正在編寫一個綁定到通用ERP解決方案中的數據庫模型的文件處理程序。
通過這種方式,我可以獲得不同文件的多個模板和一組明確區分的通用接口方法。 該接口給出了訪問方法的正確類比,而不是基本抽象類的內容。
當我為不同的文件存儲服務制作適配器時,這個實現將允許接口在完全不同的上下文中的其他地方使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.