2012-01-07 69 views
2

當是理想的:PHP類/ OOP:什麼時候「引用」類中的類與擴展類?

class USER { 
    // stuff about user 
} 

class PROFILE extends USER { 
    // stuff about user's profile 
} 

,當是理想的:

class USER { 
    $id; 
    $profile; 

    function __construct($id) { 
     $this->id = $id; 
     $this->profile = new PROFILE($id); 
     // set profile variables 
    } 
} 

class PROFILE { 
    $id; 
    // other variables pertaining to profile 

    function __construct($id) { 
     $this->id = $id; 
    } 
} 

我覺得更舒適的第二個例子嗎?有什麼特別的警告我應該知道嗎?

我是否應該將其中一個視爲不是互斥子集而另一個是兒童?

回答

2

這是encapsulationinheritance的經典問題。

使用封裝:

  • 當你想提供的基類的功能一些,但想隱藏一些。
  • 當你想擴展/修改基類的功能,但不需要它是類型兼容的。

使用繼承:

  • 當你需要派生類是類型兼容的基類。換句話說,如果客戶端代碼需要引用DerivedBase引用的實例。

在您的示例中,PROFILE從USER派生出來沒有意義。用戶具有配置文件,但配置文件不是用戶。對於這個例子,USER類包含一個PROFILE字段是有意義的。

+0

我可以假設封裝對擴展一個類來處理新協議有好處嗎?如果讓USER類擴展包含數據庫連接函數的數據庫類是否有意義? – xistva 2012-01-07 01:41:46

+0

當然,但類似的東西需要基類提供派生類將使用的某種通用功能。如果新類完全不同,但是你想像對待第一個協議處理類那樣對待它,那麼你想要定義一個接口或抽象基類,這兩個協議都是從中派生的。 – 2012-01-07 01:43:47

+2

我喜歡這個解釋。對於那些OOP的新手來說,問問自己是否繼承的類是「父類」。例如,「是用戶的個人資料?」答案是否定的,所以它不擴展USER。相反,如果它與問題「有一個」相匹配,那麼它可能是一個屬性/屬性。例如,USER「有一個」PROFILE。 – 2012-01-07 01:44:19

1

我會說,真正的方式做這將是:

class User { 

    private $id, 
      $profile; 

    public function __construct($id, Profile $profile) { 
     $this->id = $id; 
     $this->profile = $profile; 
    } 

} 

class Profile { ... } 

new User(42, new Profile(...)); 

你不想來擴展類,除非有清晰的層次結構(Admin extends User)。
你也不應該夫婦類似你在User構造函數中所做的那樣,而是使用如上所示的依賴注入。

+0

所以如果我想創建一個用戶,但不想創建一個配置文件,我可以在USER構造函數中只有Profile $ profile = NULL作爲默認值?如果我想要手動注入它?我確實看到你關於耦合類的觀點,以及爲什麼它不起作用,但是我會創建一個概要文件對象而沒有將它與用戶關聯起來。或者更確切地說,有些時候我不想訪問用戶的個人資料,只是有關用戶的基本信息,如ID和密碼。 – xistva 2012-01-07 01:50:37

+0

那麼,在你的例子中,每個用戶默認都有一個配置文件,而且你甚至不能控制該配置文件是什麼,因爲它是在用戶的構造函數中默認構造的。這是一回事,真的。但通過將配置文件注入用戶的構造函數,您可以更好地控制該配置文件的內容。您可以爲不同種類的配置文件子類/擴展'Profile'類。你可以模擬它進行單元測試。如果您的業務規則是每個用戶都必須擁有一個配置文件,那麼在構造函數中需要一個配置文件。如果它可以是可選的,可以通過'Profile $ profile = null'來選擇它。 – deceze 2012-01-07 03:20:08

1

通常,除非可以說PROFILE只是一種特殊的USER,並且在所有情況下都可以像USER一樣對待,您不希望使用繼承來建模關係。一個USER可能一個PROFILEPROFILE可能與特定USER相關,但兩者是不同的概念 - 所以沒有一個應該從其他繼承。

0

通常,父類更像是您將要擴展且很少自己使用的泛型類。所以繼承更像它應該是這樣的:你擴展一個泛型類。

我不會再去了動物的比喻,但這樣的:

class USER {} // generic class 

class ADMIN extends USER {} // specific class 

class MOD extends USER {} // specific class 

具有邏輯,因爲我們擴展了一般的用戶類指定類每種類型的用戶。

對於任何不分層次的東西,我會使用封裝。