本文目录导读:

在领域驱动设计(DDD)中,实体(Entity) 和 值对象(Value Object) 是两个核心概念,它们在PHP中的实现差异主要体现在标识(Identity)、可变性(Mutability) 和 相等性比较(Equality) 上。
以下是它们在PHP中的核心区别及代码示例:
核心区别总览
| 维度 | 实体 (Entity) | 值对象 (Value Object) |
|---|---|---|
| 标识 | 拥有唯一标识(如ID),即使属性全变,仍是同一个对象。 | 没有自己的标识,由其属性值定义。 |
| 相等性 | 通过ID比较是否相等。 | 通过所有属性值比较是否相等。 |
| 可变性 | 通常是可变的(Model),可以修改属性。 | 通常是不可变的,修改属性应返回新实例。 |
| 生命周期 | 有明确的生命周期(创建、更新、删除)。 | 无生命周期,是瞬时的,可随时创建和丢弃。 |
| 例子 | 用户、订单、文章 | 金额、地址、颜色、坐标、日期范围 |
PHP 实现示例
实体示例:用户(User)
实体的核心是 ID,就算改了名字和邮箱,$user 依然是同一个用户。
<?php
class User
{
// 实体的灵魂:唯一标识
private string $userId;
private string $name;
private string $email;
public function __construct(string $userId, string $name, string $email)
{
// 通常由外部传入或自增,保证唯一
$this->userId = $userId;
$this->name = $name;
$this->email = $email;
}
// 标识是不可变的(ID不变)
public function getUserId(): string
{
return $this->userId;
}
// 实体的属性是可变的:允许改名
public function changeName(string $newName): void
{
$this->name = $newName; // 修改了内部状态,但ID没变
}
// 相等性比较:比较ID
public function equals(?User $other): bool
{
return $other !== null && $this->userId === $other->userId;
}
}
// 使用示例
$user1 = new User('001', '张三', 'zs@example.com');
$user2 = new User('001', '李四', 'ls@example.com');
var_dump($user1->equals($user2)); // true (因为ID都是001)
// 即使属性完全改了,只要ID一样,还是同一个实体
$user1->changeName('王五');
值对象示例:货币金额(Money)
值对象的核心是 属性值组合。10美元 就是一个值,换了一百个 10美元 对象,它们代表的是同一个值。
<?php
class Money
{
// 值对象通常是不可变的:用readonly或private + getter
private readonly float $amount;
private readonly string $currency;
public function __construct(float $amount, string $currency)
{
// 可以在这里做业务校验
if ($amount < 0) {
throw new InvalidArgumentException('金额不能为负');
}
$this->amount = $amount;
$this->currency = strtoupper($currency);
}
// 只读访问
public function getAmount(): float
{
return $this->amount;
}
public function getCurrency(): string
{
return $this->currency;
}
// 相等性比较:比较所有属性
public function equals(?Money $other): bool
{
return $other !== null
&& $this->amount === $other->amount
&& $this->currency === $other->currency;
}
// 重要:修改操作返回新实例,而不是修改原对象
public function add(Money $other): Money
{
// 业务规则:只能同币种相加
if ($this->currency !== $other->currency) {
throw new InvalidArgumentException('币种不一致');
}
// 返回一个新的值对象!
return new Money(
$this->amount + $other->amount,
$this->currency
);
}
// 格式化为字符串展示
public function __toString(): string
{
return sprintf('%s %.2f', $this->currency, $this->amount);
}
}
// 使用示例
$money1 = new Money(100.00, 'USD');
$money2 = new Money(100.00, 'USD');
$money3 = new Money(100.00, 'CNY');
var_dump($money1->equals($money2)); // true (属性完全一样)
var_dump($money1->equals($money3)); // false (币种不同)
// 不可变性演示
$result = $money1->add(new Money(50.00, 'USD'));
echo $money1->getAmount(); // 输出 100.00 (原对象没变)
echo $result->getAmount(); // 输出 150.00 (新对象)
实战判断技巧
当你定义一个PHP类时,问自己以下问题:
-
它有一个自然的主键(ID)吗?
- 有 -> 很可能是 实体 (用户ID、订单号)。
- 没有 -> 很可能是 值对象 (地址、坐标)。
-
如果两个对象属性完全一样,它们可以被视为同一个东西吗?
- 可以(“张三住在[北京]” vs “李四也住在[北京]”,两个地址一样,没区别) -> 值对象。
- 不可以(“张三是ID=1” vs “李四也是ID=1”,不可能,ID必须不同) -> 实体。
-
这个对象需要在数据库里有独立的表并追踪变化吗?
- 需要(用户修改了名字,需要记录历史) -> 实体。
- 不需要(订单的地址改了,直接替换值就好,旧地址不会单独被追踪) -> 值对象。
- 实体:有 ID,可变,靠ID区分。
$user->changeName()。 - 值对象:无ID,不可变,靠属性组合区分。
$money->add()返回新Money,$address->changeCity('北京')返回新Address。
在PHP中正确区分它们,能让你的代码更符合业务语义,减少意外的状态共享bug,并提高代码的可测试性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播