请务必记住,这些是关联类和对象的模式。
我要说的第一点是,任何诸如“使用组合而不是继承”之类的总括规则都反映了一些缺失的上下文。如果合适,请使用继承。
这些其他模式用于将类关联在一起。
聚合通常用于层次/父/子关系。
一个典型的例子是关联员工和部门。
- 存在 Employee 对象
- 员工是部门“拥有/成员”
- 您可以说“Department Foo”聚合了 0-N 名员工。
因此,为了对此进行建模,Department 通常会有一个内部 employees[] 数组。
相反,员工可以在内部存储部门对象。这基本上匹配了两个类之间的关系为 Many >-
组合反映了更严格的父/子关系,暗示 ObjectA 由 1-N 个 Object B 组成。
组合关系的经典示例是购物车,其中包含 0-N 个产品线项。
假设 Cart 和 Lineitem 的类,Cart 将存储 Lineitem 数组。
Aggregation 和 Composition 之间的主要区别在于,Composition if 销毁了 Cart,那么所有关联的 Lineitem 对象都应该被销毁。
在聚合示例中,如果一个部门被删除,这并不一定意味着所有聚合的员工都会被删除,而是一个部门的删除只会破坏员工与部门之间的关系,并且可能需要存储员工部门对象的变量将为空,直到员工被分配到另一个部门,或者他们的工作结束。
实际上,由于 PHP 是页面范围的,因此变量和对象组合的寿命很短,因此在使用这些 UML 模式的 ORM 之外,您真的找不到很多情况。数据需要持久化,并且通常通过某种 RDBMS 或 Document 数据库进行持久化,其中 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,因为构造函数始终是公开的。
$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)的基础,以及您在 Gang of 4 book、Domain Driven Design 中找到的其他常见 OOP 设计模式的实现。或其他 books 和 websites 这些天。