什么时候最理想:
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;
}
}
我觉得第二个例子更舒服?有什么特别的注意事项我应该知道吗?
我应该把一个看作不是互斥的子集,把另一个看作孩子吗?
这是封装和继承的经典问题。
使用封装:
- 当您想提供基类的一些功能,但又想隐藏其中的一些功能时
- 当您希望扩展/修改基类的功能,但不需要它与类型兼容时
使用继承:
- 当您需要派生类与基类的类型兼容时。换句话说,如果客户端代码需要引用具有
Base
引用的Derived
实例
在您的示例中,PROFILE从USER派生是没有意义的。用户具有配置文件,但配置文件不是用户。对于本例,USER类包含一个PROFILE字段是有意义的。
我认为真正的方法是:
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
构造函数中那样耦合类,而应该使用如上所示的依赖注入。
通常,除非您可以说PROFILE
只是USER
的一种特殊类型,并且在所有情况下都可以像对待USER
一样对待,否则您不希望使用继承来为关系建模。USER
可能具有PROFILE
,或者PROFILE
可能与特定USER
关联,但两者是不同的实体,因此两者都不应继承。
通常父类更像是要扩展的泛型类,很少单独使用。因此,继承更像是它应该有的样子:扩展一个泛型类。
我不会再使用动物的比喻了,而是类似于:
class USER {} // generic class
class ADMIN extends USER {} // specific class
class MOD extends USER {} // specific class
有一个逻辑,因为我们扩展了一个通用的USER类,为每种类型的用户指定一个类。
对于任何其他不分层的东西,我都会使用封装。