2011-11-21 104 views
26

當您繼承對象並想擴展初始化代碼時,有兩種方法。重寫__construct(),並實現一個你的超類構造函數調用的初始化方法。最佳實踐,覆蓋__construct()與提供init()方法

方法1:

class foo 
{ 
    public function __construct ($arg1, $arg2, $arg3) 
    { 
     // Do initialization 
    } 
} 

class bar extends foo 
{ 
    public function __construct ($arg1, $arg2, $arg3) 
    { 
     parent::__construct ($arg1, $arg2, $arg3); 
     // Do subclass initialization 
    } 
} 

方法2

class foo 
{ 
    public function init() 
    { 
     // Dummy function 
    } 

    public function __construct ($arg1, $arg2, $arg3) 
    { 
     // Do subclass defined initialization 
     $this -> init(); 
     // Do other initialization 
    } 
} 

class bar extends foo 
{ 
    public function init() 
    { 
     // Do subclass initialization 
    } 
} 

爲Zend框架的文件似乎阻止重載構造函數,並希望您覆蓋的init方法,若設置,但是這在某種程度上只是沒有按」對我感覺不錯。 Zend也傾向於做一些我不喜歡的事情,所以我不確定它是否應該被用作最佳實踐的例子。我個人認爲第一種方法是正確的,但我經常看到第二種方法足以讓人懷疑這是否應該是我應該做的。

您對覆蓋__construct有任何意見嗎?我知道你必須小心地記得調用超類的構造函數,但大多數程序員應該知道這一點。

編輯:我不使用Zend ,我只使用它作爲鼓勵您使用的init(),而不是壓倒一切的__construct()代碼庫的一個例子。

回答

16

看起來像第二種方法推遲了這個問題。

如果你有一個類:

class bar2 extends bar // which already extends foo 
{ 
    public function init() 
    { 
    // You should then do anyway: 
    parent::init(); 

    // ... 
    } 
} 

我會去的第一種方法太,更合理,更簡單,因爲parent::init()parent::__construct()呼叫無法避免不休。第一種方法,海事組織,不那麼容易混淆。

3

首先

Zend公司也傾向於做一些事情,我不是很滿意

你可以簡單的解決這個問題,通過不使用它。

但第二,更重要的是你應該重寫init(),而不是__construct()因爲init()是Zend的用途和使用它確保您的應用程序的其餘部分是存在的,到位的調度操作的一部分。否則會打破Zend的MVC模型的流程,並可能導致奇怪的行爲。

編輯

我覺得對我來說最主要的原因是它停止其他開發人員擺弄。你可以用init()做任何事情,但不能用__construct()做任何事情,因爲這需要正確運行所有正確的參數。

這是從Zend文檔:

雖然你可以總重寫動作控制器的構造函數,我們 不建議這樣做。Zend_Controller_Action :: _ construct()執行 一些重要任務,例如註冊請求和響應 對象,以及從前端控制器傳入的任何自定義調用參數。如果您必須覆蓋構造函數,請確保調用parent :: _construct($ request,$ response,$ invokeArgs)。

+0

對不起,我應該在我的問題上更清楚一點。我沒有使用Zend,如果我覺得它可能有用,我正在編寫我計劃在將來某個時候向公衆發佈的代碼。我只是使用Zend作爲例子,因爲它傾向於使用init方法,並且如果一個主要框架正在這樣做,那麼肯定有一個原因。 (即使我個人不喜歡這個問題的框架太多) – GordonM

+0

啊對了。可能值得編輯,並把它放在你的問題中。我通過回答編輯。 –

0

我可以看到一個受益於使用init(),而不是__construct():如果在構造函數簽名的變化,你將不得不更新每一個派生類以匹配新的簽名。

如果init()沒有參數,則不會發生。

+1

如果你有一個帶有init()實現的子類,那麼這也不是真的嗎?這些子類是不是也必須更新以反映超類初始化方法的變化? – GordonM

1

我會使用init函數,因爲如果您重寫構造函數,您(通常)必須記得調用父級構造函數,在子類的構造函數的頂部。雖然您可能會意識到這一點,但您不能保證另一位開發人員負責維護您的應用程序。

8

我可以想到的唯一兩種情況是使用init()時,您的構造函數是非公共的,但您需要讓人們有機會影響初始化,例如,在一個抽象的Singleton中(你不想使用它)。或者,就像在Zend Framework中一樣,額外的初始化應該被拒絕(但是您不會從構造函數中調用init())。

順便說一句,從超類調用子類中的方法稱爲Template Method。 UseCase將協調某個工作流程,但允許子類型影響其中的一部分。儘管這通常是通過常規方法完成的。請注意,你的構造函數不應該編排任何東西,只是initialize the object into a valid state

你一定要呼叫/報價init()從構造以防止不必記住調用父類的構造函數的開發。雖然這聽起來很方便,但它會很快破壞繼承層次結構。還要注意它不同於通常初始化對象的方式,開發人員必須學習這種新行爲,就像他們必須學會調用超類型的構造函數一樣。

+0

我不想使用單身人士和註冊表之類的東西,我在工作中得到了足夠的東西,它並沒有讓我的生活變得更輕鬆:)延期初始化解決方案看起來像是它唯一真正有效的用途接近我,即使如此,我覺得它應該謹慎使用。順便指點一下 – GordonM

+0

,另一個人的回答得到了接受,因爲我懷疑你現在並不需要代表。 :) – GordonM

+0

@GordonM meh,在這裏,我在想最有用的答案會得到綠色的勾號:P – Gordon