PHP实体和值对象区别是什么

wen PHP项目 1

本文目录导读:

PHP实体和值对象区别是什么

  1. 核心区别总览
  2. PHP 实现示例
  3. 实战判断技巧

在领域驱动设计(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类时,问自己以下问题:

  1. 它有一个自然的主键(ID)吗?

    • -> 很可能是 实体 (用户ID、订单号)。
    • 没有 -> 很可能是 值对象 (地址、坐标)。
  2. 如果两个对象属性完全一样,它们可以被视为同一个东西吗?

    • 可以(“张三住在[北京]” vs “李四也住在[北京]”,两个地址一样,没区别) -> 值对象。
    • 不可以(“张三是ID=1” vs “李四也是ID=1”,不可能,ID必须不同) -> 实体。
  3. 这个对象需要在数据库里有独立的表并追踪变化吗?

    • 需要(用户修改了名字,需要记录历史) -> 实体。
    • 不需要(订单的地址改了,直接替换值就好,旧地址不会单独被追踪) -> 值对象。
  • 实体:有 ID可变,靠ID区分。$user->changeName()
  • 值对象:无ID,不可变,靠属性组合区分。$money->add() 返回新 Money$address->changeCity('北京') 返回新 Address

在PHP中正确区分它们,能让你的代码更符合业务语义,减少意外的状态共享bug,并提高代码的可测试性。

抱歉,评论功能暂时关闭!