2012-04-23 143 views
4

我試圖設計一些類層次結構,並在此部分「卡住」。抽象類 - 兒童類型

比方說,我有以下類

abstract class Video 
{ 
    const TYPE_MOVIE = 1; 
    const TYPE_SHOW = 2; 

    abstract public function getTitle(); 
    abstract public function getType(); 
} 

class Movie extends Video 
{ 
    // ... 

    public function getType() 
    { 
     return self::TYPE_MOVIE; 
    } 
} 

class Show extends Video 
{ 
    // ... 

    public function getType() 
    { 
     return self::TYPE_SHOW; 
    } 
} 

在系統我有(分析器)類的有多種不同的組成部分,它封裝 電影放映對象的創建和返回OBJ。給客戶。

問題:什麼是獲得一個obj類型的最佳方式。從解析器/工廠類返回,使客戶可以這樣做

$video = $parser->getVideo('Dumb and Dumber'); 

echo $video->getTitle(); 

// Way 1 
if($video->getType == 'show') { 
    echo $video->getNbOfSeasons(); 
} 

// Way 2 
if($video instanceof Show) { 
    echo $video->getNbOfSeasons(); 
} 

// Current way 
if($video->getType == Video::TYPE_SHOW) { 
    echo $video->getNbOfSeasons(); 
} 

難道還有比我的解決方案更好的辦法(讀作:沒有我的解決方案吸)?

+2

+1完美的例子來解釋任何問題...... – 2012-04-23 08:50:28

回答

2

有沒有比我的解決方案更好的辦法(讀作:我的解決方案是吸?)?

您的解決方案本身並不吸引人。但是,每當有人試圖確定子類型以執行某些操作時,我傾向於懷疑;爲什麼?這個答案可能有點理論上的,甚至有點迂腐,但是這裏有。

你應該不在乎。父類和子類之間的關係是子類覆蓋父類的行爲。 A parent class should always be substitutable by it's children, regardless which one。如果你發現自己在問:我怎麼確定亞型,你平時做的兩兩件事之一「錯誤」:

  1. 您正在嘗試完成基於亞型的作用。通常情況下,人們會選擇將該行動轉移到班級本身,而不是班級的「外部」。這也使得代碼更易於管理。

  2. 您試圖通過使用繼承來解決您自己介紹的問題,其中繼承不受保證。如果有父母,並且有孩子,每個孩子都有不同的使用方式,每個孩子都有不同的方法,只是停止使用繼承。它們不是同一類型。一部電影與電視連續劇不太一樣,甚至沒有接近。當然,你可以在電視上看到兩者,但相似之處在那裏停止。

如果您遇到問題2,您可能使用繼承不是因爲它有意義,而只是爲了減少代碼重複。這本身就是一件好事,但你試圖這樣做的方式可能不是最佳的。如果可以的話,你可以使用構圖來代替,儘管我懷疑重複的行爲在哪裏,除了一些任意的getter和setter。

也就是說,如果你的代碼有效,並且你對此感到滿意:那就去吧。這個答案在如何處理面向對象的時候是正確的,但我對你的其他應用程序一無所知,所以答案是通用的。

+0

我完全同意你的看法。 2.正確地指出了擴展和實現之間的區別。一部電影和一部電影可能會遵循一個共同的界面,允許在電視上播放,但他們可能不會再分享更多內容。 – 2012-04-23 09:22:36

+0

@Berry Langerak與此相關的交易是客戶通過傳遞一些電影名稱來請求視頻,在後面我搜索/獲取/解析數據,並創建並填充正確的obj。取決於所獲取的數據(它可以是電影,電視節目,以及將來也許還有一些附加類型),問題是我不在乎什麼類型的obj。我創建了,但是請求obj的客戶端。問津。他需要知道他可以調用什麼方法。 – 2012-04-23 12:34:43

+0

...所以即使電影和電視節目不共享相同的基類,客戶端仍然需要檢查哪個obj。 (視頻類型)他得到了。也許我會把整個事情都包裝在Facade類中。無論如何,你讓我停下來,從頭開始考慮整個事情:) – 2012-04-23 12:34:56

2

我會走的方式2.它摘要你需要添加另一個常數在Video萬一你可能想要添加class SoapOpera extends Show(例如)。

使用方式#2,您對常量的依賴性降低。無需對其進行硬編碼即可獲得的任何信息,意味着未來可能發生的問題在希望擴展的情況下更少。閱讀關於Tight an Loose Coupling

+1

同意 - 做他們現在做的事情的唯一好處是,它允許一個額外的控制 - 極度的水平,如果你有客戶開發誰不是't「允許」改變抽象類 - 使用抽象中的常量可以讓其他開發人員知道它們如何被允許擴展基礎。 – CD001 2012-04-23 08:53:58

1

我認爲第二個選擇更好,使用instanceof。這對於所有OOP設計來說通常是普遍的,而不僅僅是PHP。

第一個選項是基類中派生類的具體細節,因此必須修改每個添加的新派生類的基類,這應該總是避免。

當添加新的派生類時,保持基類不變,促進了代碼重用。

1

如果有一個「正確」的方式,並且在編碼過程中一切都是主觀的(只要它不會對性能/可維護性產生不利影響)),那麼它就是作爲「真相」和「布雷迪」的第二種方式「已經指出。

現在做事情的方式(抽象中的類常量)的好處是,當您與其他開發人員一起工作時,它可以提供有關您如何期望抽象類與。

例如:

$oKillerSharkFilm = Video::factory(Video::MOVIE, 'Jaws', 'Dundundundundundun'); 
$oKillerSharkDocumentary = Video::factory(Video::DOCUMENTARY, 'Jaws', 'A Discovery Shark Week Special'); 

當然,缺點是,你必須保持在抽象類中的「允許的擴展名」。

您仍然可以使用instanceof方法,如問題中所示,並在摘要中保留允許的擴展名列表,主要用於控制/類型修復。