2014-10-08 46 views
2

我有一個抽象類,我繼承:抽象的誆騙 - PHP

abstract class Test 
{ 
    public function GetTests() 
    { 
    } 
} 

,我有,我一直在使用抽象類實現了大部分的時間,具體的:

class Concrete extends Test 
{ 
    // No problemmos 
} 

我最近不得不實行不同版本的GetTests方法,實際上我想覆蓋它,因爲它是內置到我所有的路由:

class Concrete extends Test 
{ 
    public function GetTests($newArgument) 
    { 
     // notice $newArgument 
    } 
} 

不過,我收到此錯誤信息:

Declaration of Concrete::GetTests() should be compatible with Test::GetTests() 

除了從抽象類複製的功能全部用於該混凝土,即使我只需要以不同的方式實現這一個方法...有什麼辦法解決這個問題?

我也明白,我可以有:

abstract class Test 
{ 
    abstract public function GetTests(); 
} 

但是,這就是爲什麼我誆騙,因爲我不再需要修改底層測試類是如何實現的?衛生署的能力..除非我真的有..


感謝所有偉大的答案...

我已決定不再將自己的斯諾克(它會受到傷害,但這將會是值得的),我將在Concrete類中實例化Test類,實現所有Test類方法的具體版本,然後在其中調用實例化的Test類...這意味着在未來的(或者現在確實)我根本無法調用該功能...

對於背景:

/* no longer abstract */ class UnitOfWorkController 
{ 
    public function GetUnits() 
    { 
     // Implementation 
     return View::make(...); 
    } 
} 

和...

class SomethingController /* no longer extends the UnitOfWorkController */ 
{ 
    private $unitOfWorkController; 
    public function __Construct() 
    { 
     $this->unitOfWorkController = new UnitOfWorkController(); 
    } 

    public function GetUnits() 
    { 
     return $this->unitOfWorkController->GetUnits(); 
     // or I could just implement my own junk 
    } 
} 
+4

由於'$ newArgument',它不兼容。您可以將此參數添加到摘要中,也可以將其從「混凝土」類中移除;但事實上,它必須兼容 – Brewal 2014-10-08 14:22:00

+0

只是爲了看看我是否理解得當:你希望能夠覆蓋抽象類的功能嗎?如果我理解正確,您必須根據哪個版本的php製作您的'$ newArgument'選項(來自[此文檔](http://php.net/manual/en/language.oop5.abstract.php)),而不是聲明它並試圖用'func_get_args'獲得它可能? (真的不確定這最後一點) – Tensibai 2014-10-08 14:30:23

+0

我不知道是誰回答,但我已經把我的解決方案放到OP的末尾,請給我反饋:) – Jimmyt1988 2014-10-08 15:25:09

回答

1

您的具體子類違反了Liskov Substitution Principle,它簡短地說明了如果一個類X的對象可以被給定的代碼片段處理,那麼X的每個可能的子類也必須能夠由同一段代碼處理。

假設我想創建Test的另一個子類並想實現我自己的GetTests方法。基類方法根本不接受任何參數,所以這表明,如果我的子類可以替代其超類,那麼我的該方法的實現也不能接受任何參數。如果我給出我的實現參數,那麼它不再符合超類規定的規範。

如果我有代碼,不會:

$object = new Test; 
$test -> GetTests(); 

話,我不能代替我的測試的子類,而還改變調用代碼在參數來傳遞。同樣,如果我改變它,那麼我有另一個Test的子類,它不需要GetTests的參數,那麼代碼將不得不再次改變。實際上,同樣的代碼根本不能像兩個子類一樣使用,而不必跳過一些箍環來確定實際的類,並使用適當的調用約定,這意味着需要知道關於我將要使用的類的事情我不需要知道。

對於與其超類相匹配的子類方法簽名,PHP並不比大多數OO語言嚴格,但如果它們不匹配,它將發出警告。修復警告的唯一方法是讓所有子類與它們繼承的超類具有相同的方法簽名。

1

子方法必須具有相同的簽名作爲父類中的相同方法。這包括所需的參數及其類型。

例如,以下方法的子類也必須具有一個參數,並且該參數必須轉換爲ArgumentType類或其子類。

public function something(ArgumentType $Argument) 
{ 
} 

可以,但是,使參數可選將其設置爲null或任何其他值:

public function something(ArgumentType $Argument = null) 
{ 
} 

在這種情況下,孩子的方法可以忽略此參數。

從PHP文檔,見http://php.net/manual/en/language.oop5.abstract.php

[...]此外的方法的簽名必須匹配,即類型的提示和所需參數的數量必須是相同的。例如,如果子類定義了可選參數(抽象方法的簽名不存在),則簽名中不會有衝突。

1

方法簽名Concrete::GetTests()有一個變量,而Test::GetTests()沒有。由於您已經在Test中定義了此方法,因此它現在被繼承。繼承的版本與您的重寫版本不兼容。

這裏是你的選擇:

  1. 添加$newArgument的參數列表中Test::GetTests()
  2. Concrete::GetTests()中的參數列表中刪除$newArgument
  3. Concrete::GetTests()重命名爲其他內容。
1

PHP不支持這個,如錯誤消息所述。如果你想覆蓋的功能,它必須具有相同的足跡,這在你的情況下,它不會

你可以做的是用魔術方法:http://php.net/manual/en/language.oop5.overloading.php#object.call

參數數組是一個獨立的實體,所以你在代碼中「決定」(你可以覆蓋)如何處理哪個參數。

我想鏈接一個博客,我閱讀了這篇文章,但找不到我正在考慮的博客。有this rather strangely formatted one,不知道它是否有用,但它確實涉及到一些問題。

你顯然可以將參數添加到父項,但是這是「泄漏」向上。如果其他孩子想要更多,你會得到一個隨機參數的大派對,所有參數都可以被忽略。