2014-02-09 118 views
2

的PHP OOP聲明我有以下OOP結構:接口實現兼容性

<?php 


interface AnimalInterface 
{ 
    public function getName(); 
} 

class Dog implements AnimalInterface 
{ 
    public function getName() { 
     return 'dog'; 
    } 

    public function makeFriends() 
    { 
     echo 'I have friends now :)'; 
    } 
} 

class Cat implements AnimalInterface 
{ 
    public function getName() { 
     return 'cat'; 
    } 

    public function hateFriends() 
    { 
     echo 'I cant make friends :('; 
    } 
} 

interface AnimalDoInterface 
{ 
    public function makeFriend(AnimalInterface $animal); 
} 

class DogFriend implements AnimalDoInterface 
{ 
    public function makeFriend(Dog $dog) 
    { 
     $dog->makeFriends(); 
    } 
} 

class CatFriend implements AnimalDoInterface 
{ 
    public function makeFriend(Cat $cat) 
    { 
     $cat->hateFriends(); 
    } 
} 

現在PHP對Object Interfaces手冊說:

The class implementing the interface must use the exact same method signatures as are defined in the interface. Not doing so will result in a fatal error.

爲什麼會出現這種情況?我完全誤解了界面嗎?當然,我應該能夠聲明AnimalDoInterface::makeFriend任何接口或接口的實現?在這種情況下,它應該在技術上與Cat implements AnimalInterface兼容,這正是它期望的。

無論我是否讓我的OOP出錯,是否有一種方法可以在PHP中實現這一點?

所以看來我還不夠清楚,我的不好。然而,基本上我試圖實現的是讓AnimalDoInterface的實現比接口說的更具限制性。所以在這種情況下,我想DogFriend::makeFriend只允許Dog類作爲它的參數,在我看來應該是可以接受的,因爲它實現了AnimalInterface,並且CatFriend允許Cat類,這也是同樣的事情。

編輯:修正了類,還增加了我試圖實現的內容。

編輯2:此刻

所以,我必須實現它的方法是如下:

class DogFriend implements AnimalDoInterface 
{ 
    public function makeFriend(AnimalInterface $dog) 
    { 
     if(!($dog instanceof Dog)) { 
      throw new \Exception('$dog must be of Dog type'); 
     } 
     $dog->makeFriends(); 
    } 
} 

class CatFriend implements AnimalDoInterface 
{ 
    public function makeFriend(AnimalInterface $cat) 
    { 
     if(!($dog instanceof Cat)) { 
      throw new \Exception('$dog must be of Cat type'); 
     } 
     $cat->hateFriends(); 
    } 
} 

我想有以避免類此額外的檢查類型。

+0

我不認爲我跟着你說的任何話。什麼是實際問題?「方法簽名」是指方法名稱,參數(帶有類型提示),可見性(公共/私人)以及它是否是靜態的。 –

+0

不完全清楚你在問什麼。你是否嘗試運行上面的代碼?它會與錯誤'致命錯誤:類狗包含1抽象方法,因此必須聲明爲抽象或實現其餘方法(AnimalInterface :: getAnimalName)' –

+1

實施'AnimalInterface'類沒有方法'getAnimalName()'哪是抽象的。並且必須使用。請記住,所有接口方法本身都是抽象的,並且必須在類中實現。 –

回答

4

接口的唯一工作是強制執行這樣一個事實,即兩個對象的行爲方式完全相同,無論它們如何實現該行爲。這是一個合同,聲明兩個對象爲了某些特定目的可以互換。

接口AnimalInterface定義的行爲(功能)getAnimalName(),並聲稱要實現該接口必須實現行爲的任何類(編輯這部分代碼已經被糾正,但作爲一個很好的介紹)。 class Dogimplements AnimalInterface一起聲明,但未實現所需的行爲 - 您不能在Dog的實例上調用getAnimalName()。所以我們已經有一個致命的錯誤,因爲我們還沒有遇到由接口定義的「契約」。

固定這一點,程序中,你就必須具有的makeFriend(AnimalInterface $animal)定義的行爲(功能)的接口AnimalDoInterface - 含義,可以傳遞任何對象它實現AnimalInterface任何對象的makeFriend方法,該方法實現了AnimalDoInterface

但是,您隨後定義了class DogFriend,這是一種更具限制性的行爲 - 其版本makeFriend只能接受Dog對象;根據接口它也應該能夠接受Cat對象,它們也實現了AnimalInterface,所以再一次,接口的「契約」沒有得到滿足,我們會得到一個致命的錯誤。

如果我們要解決這個問題,有一個在您例如一個不同的問題:你有一個呼叫$cat->hateFriends();但如果你的論點是AnimalInterface類型或AnimalDoInterface,你就沒有辦法知道一個hateFriends()功能存在。對於這樣的事情,PHP相當放鬆,如果事實證明它不存在,PHP會在運行時嘗試並放棄;更嚴格的語言只會讓你使用保證存在的函數,因爲它們是在接口中聲明的。


要理解爲什麼你不能比接口更嚴格,想象你不知道類特定對象,所有你知道的是,它實現了特定的接口。

如果我知道對象$a實現AnimalInterface和對象$b器具AnimalDoInterface,我可以做如下假設,只是在看接口:

  1. 我可以調用$a->getName();(因爲AnimalInterface具有在其合同)
  2. 我可以調用$b->makeFriend($a);(因爲AnimalDoInterface在其合同,我可以通過什麼實現AnimalInterface

但是對於您的代碼,如果$aCat,並且$bDogFriend,我的假設#2將會失敗。如果界面發生這種情況,它不會做它的工作。

+0

謝謝,所以第一個問題並不是要解決問題,而是第二個問題。我已經更新了我的問題,以解釋我正在努力實現的目標。 –

+0

@HoshSadiq我已經添加了一個額外的部分,這可能會讓你更清楚爲什麼界面不能讓你做你想做的事情。 – IMSoP

+0

我明白,但肯定應該可以使某個論點更具限制性,這樣使得實現更具限制性的類,即在這種情況下,即「DogFriend」,那麼它仍然知道它具有「收縮」方法,還有其他特定於「Dog」的方法?看到我編輯的問題,雖然它不是理想的,但它是一種解決方法,雖然在我看來是一個醜陋的問題。根據你所說的,它不應該是這樣的,或者我錯了? –

3

所有實現接口的類都必須具有相同的方法,以便您可以在對象上調用該方法,而不管實例化哪個子類型。

在這種情況下,您有一個邏輯不一致性,因爲兩個子類型貓和狗有不同的方法。所以你不能在對象上調用makeFriends(),因爲你不知道那個對象那個方法。

這就是使用接口的要點,所以你可以使用不同的子類型,但同時你至少可以確定一些常用的方法。

在你的情況下處理這種情況的一種方法是確保Cat實現相同的方法,但是使該方法在運行時拋出一個異常,表明這是不可能的。這使得接口可以在編譯時滿足(我知道PHP沒有編譯器,但是這是模仿像編譯時進行接口檢查的Java這樣的語言)。

class Cat implements AnimalInterface 
{ 
    public function makeFriends() 
    { 
     throw new RuntimeException('I cant make friends :('); 
    } 
} 
+1

PHP肯定有一個編譯器,並且確實會在編譯時做這樣的檢查,正如你可以看到的那樣,如果你包含一個文件中有一個糟糕的'class'定義。 (你只是不需要手動調用編譯器,除非你使用「操作碼緩存」,否則它通常會在每次加載時重新處理該文件。) – IMSoP

+0

對不起,請參閱更新的問題,我不小心發佈了一個不完整的問題題 :) –

0

它實現AnimalDoInterface的類必須具有makeFriend方法這需要一個實現AnimalInterface任何對象。在你的情況下,試圖申報

class DogFriend implements AnimalDoInterface { 
    public function makeFriend(Dog $foo) { } 
} 

不會準確地實現這一點,因爲它應該始終是安全的任何人都可通過它實現AnimalInterface到的任何東西makeFriend方法,實現了AnimalDoInterface。