PHP 中的组合与聚合
Composition vs Aggregation in PHP
我正在阅读一篇关于 OOP 中的关系、关联、组合、聚合等的文章。有些东西令人困惑,我一直在网上找到相互矛盾的信息,所以我希望有人能对此有所了解。
所以在PHP中,我们调用下面的代码composition&很多articles/tutorials指出要使用composition继承。
class A
{
}
class B
{
public function __construct(protected A $a)
{
}
}
看了几篇关于组合和聚合的文章后,似乎以上实际上是聚合而不是组合的例子,因为在组合中 class A 的对象没有 class 就不可能存在B,所以当class B的对象被销毁时,class A的对象也应该被销毁。上面的代码显然不是这样,因为classA的对象可以存在于classB之外,所以它的生命周期不依赖于classB。
下面是组成的例子:
class B
{
public A $a;
public function __construct()
{
$this->a = new A();
}
}
所以根据我的理解,聚合意味着 class A 的对象可以存在于 class B 之外,而组合意味着 class A 的对象的生命周期取决于 class B & 不能存在于 class B.
之外
我理解正确吗?
请务必记住,这些是关联 类 和对象的模式。
我要说的第一点是,任何笼统的规则,如“使用组合而不是继承”,都反映了一些缺失的上下文。如果合适就使用继承。
这些其他模式用于将 类 联系在一起。
聚合 通常用于 hierarchy/parent/child 关系。
一个典型的例子是关联员工和部门。
- 存在 Employee 对象
- 一名员工是“部门by/a成员”
- 你可以说“Department Foo”聚合了 0-N 名员工。
因此,为了对此建模,Department 通常会有一个内部 employees[] 数组。
相反,员工可以在内部存储部门对象。这本质上匹配了两者之间的关系 类 as Many >-< Many.
Composition 反映了更严格的 parent/child 关系,其含义是 ObjectA 由 1-N 个 Object B 组成。
组合关系的经典示例是购物车,其中包含 0-N 个产品线项目。
假设 类 购物车和 Lineitem,购物车将存储一个 Lineitem 数组。
聚合和组合之间的主要区别在于,组合 如果 购物车被销毁,那么所有关联的 Lineitem 对象都应该被销毁。
对于聚合示例,如果一个部门被删除,这并不一定意味着所有聚合的员工都将被删除,而是删除一个部门只会破坏员工与部门之间的关系,并且可能需要存储员工部门对象的变量将为空,直到员工被分配到另一个部门,或者他们的雇佣关系可能结束。
实际上,由于 PHP 是页面范围的,变量和对象的组合是短暂的,所以你可能在 ORM 之外找不到很多使用这些 UML 模式的案例。数据需要持久化,通常这种持久化是通过某种 RDBMS 或文档数据库实现的,其中 ORM 将模拟表之间的关系,或者可能是层次结构。
你的例子:
这些 foo/bar A/B 示例没有语义意义或价值。示例 #1 中唯一的机制是 ObjB 必须首先是 ObjA 才能构建。您提供的语法是 PHP8 中的新语法,我一开始没注意到。
似乎这个例子更可能需要:
class A
{
private bSet = array();
public function addB(B $b) {
$this->bSet[] = new B($this);
}
}
class B
{
private $a;
public function __construct(A $a)
{
$this->a = $a;
}
}
// B shouldn't be made without a Parent A
$objA = new A();
$objA->addB();
$objA->addB();
然而,从技术上讲,没有什么能阻止你制作 B,因为构造函数总是 public。
$objB = new B($objA);
对于聚合,这将是更可能的情况:
class A
{
private bSet = array();
public function addB(B $b) {
$this->bSet[] = $b;
$b->setA($this);
}
public function delB(B $b) {
for ($i=0; $i < count($this->bSet); $i++) {
if ($b === $this->bSet[$i]) {
unset($this->bSet[$i];
break;
}
}
}
}
class B
{
private $a;
public function setA(A $a) {
if ($this->a) {
$this->a->delB($this);
}
$this->a = $a;
}
public function getA() {
return $this->a;
}
}
$objA1 = new A();
$objA2 = new A();
$objB1 = new B();
$objB2 = new B();
$objB3 = new B();
$objA1->addB($objB2);
$objA2->addB($objB1);
$objA2->addB($objB3);
//Change $objB3's parent from A1 to A2.
$objB3->setA($objA1);
A 和 B 彼此独立,但仍然拥有所有权。
我发现在 PHP 开发中更实际有价值的是依赖注入作为领先 PHP 框架(Symfony,Laravel)以及其他常见 OOP 实现的基础这些天您在 Gang of 4 book, Domain Driven Design, or other books and websites 中找到的设计模式。
我正在阅读一篇关于 OOP 中的关系、关联、组合、聚合等的文章。有些东西令人困惑,我一直在网上找到相互矛盾的信息,所以我希望有人能对此有所了解。
所以在PHP中,我们调用下面的代码composition&很多articles/tutorials指出要使用composition继承。
class A
{
}
class B
{
public function __construct(protected A $a)
{
}
}
看了几篇关于组合和聚合的文章后,似乎以上实际上是聚合而不是组合的例子,因为在组合中 class A 的对象没有 class 就不可能存在B,所以当class B的对象被销毁时,class A的对象也应该被销毁。上面的代码显然不是这样,因为classA的对象可以存在于classB之外,所以它的生命周期不依赖于classB。
下面是组成的例子:
class B
{
public A $a;
public function __construct()
{
$this->a = new A();
}
}
所以根据我的理解,聚合意味着 class A 的对象可以存在于 class B 之外,而组合意味着 class A 的对象的生命周期取决于 class B & 不能存在于 class B.
之外我理解正确吗?
请务必记住,这些是关联 类 和对象的模式。
我要说的第一点是,任何笼统的规则,如“使用组合而不是继承”,都反映了一些缺失的上下文。如果合适就使用继承。
这些其他模式用于将 类 联系在一起。
聚合 通常用于 hierarchy/parent/child 关系。
一个典型的例子是关联员工和部门。
- 存在 Employee 对象
- 一名员工是“部门by/a成员”
- 你可以说“Department Foo”聚合了 0-N 名员工。
因此,为了对此建模,Department 通常会有一个内部 employees[] 数组。
相反,员工可以在内部存储部门对象。这本质上匹配了两者之间的关系 类 as Many >-< Many.
Composition 反映了更严格的 parent/child 关系,其含义是 ObjectA 由 1-N 个 Object B 组成。
组合关系的经典示例是购物车,其中包含 0-N 个产品线项目。
假设 类 购物车和 Lineitem,购物车将存储一个 Lineitem 数组。
聚合和组合之间的主要区别在于,组合 如果 购物车被销毁,那么所有关联的 Lineitem 对象都应该被销毁。
对于聚合示例,如果一个部门被删除,这并不一定意味着所有聚合的员工都将被删除,而是删除一个部门只会破坏员工与部门之间的关系,并且可能需要存储员工部门对象的变量将为空,直到员工被分配到另一个部门,或者他们的雇佣关系可能结束。
实际上,由于 PHP 是页面范围的,变量和对象的组合是短暂的,所以你可能在 ORM 之外找不到很多使用这些 UML 模式的案例。数据需要持久化,通常这种持久化是通过某种 RDBMS 或文档数据库实现的,其中 ORM 将模拟表之间的关系,或者可能是层次结构。
你的例子:
这些 foo/bar A/B 示例没有语义意义或价值。示例 #1 中唯一的机制是 ObjB 必须首先是 ObjA 才能构建。您提供的语法是 PHP8 中的新语法,我一开始没注意到。
似乎这个例子更可能需要:
class A
{
private bSet = array();
public function addB(B $b) {
$this->bSet[] = new B($this);
}
}
class B
{
private $a;
public function __construct(A $a)
{
$this->a = $a;
}
}
// B shouldn't be made without a Parent A
$objA = new A();
$objA->addB();
$objA->addB();
然而,从技术上讲,没有什么能阻止你制作 B,因为构造函数总是 public。
$objB = new B($objA);
对于聚合,这将是更可能的情况:
class A
{
private bSet = array();
public function addB(B $b) {
$this->bSet[] = $b;
$b->setA($this);
}
public function delB(B $b) {
for ($i=0; $i < count($this->bSet); $i++) {
if ($b === $this->bSet[$i]) {
unset($this->bSet[$i];
break;
}
}
}
}
class B
{
private $a;
public function setA(A $a) {
if ($this->a) {
$this->a->delB($this);
}
$this->a = $a;
}
public function getA() {
return $this->a;
}
}
$objA1 = new A();
$objA2 = new A();
$objB1 = new B();
$objB2 = new B();
$objB3 = new B();
$objA1->addB($objB2);
$objA2->addB($objB1);
$objA2->addB($objB3);
//Change $objB3's parent from A1 to A2.
$objB3->setA($objA1);
A 和 B 彼此独立,但仍然拥有所有权。
我发现在 PHP 开发中更实际有价值的是依赖注入作为领先 PHP 框架(Symfony,Laravel)以及其他常见 OOP 实现的基础这些天您在 Gang of 4 book, Domain Driven Design, or other books and websites 中找到的设计模式。