2012-01-07 51 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 

具有逻辑,因为我们扩展了一般的用户类指定类每种类型的用户。

对于任何不分层次的东西,我会使用封装。