所有值对象都应该是不可变的

时间:2014-04-25 10:29:47

标签: php oop domain-driven-design

我有一个Employee实体,其中包含ContactInformation的值对象:

Employee.php

class Employee {
    private $contactInformation;

    public function __construct(ContactInformation $contactInformation) {
        $this->contactInformation = $contactInformation;
    }

    public function getContactInformation() {
        return $this->contactInformation;
    }
}

ContactInformation.php

class ContactInformation {
    private $email;        // Required
    private $cellPhone;    // Required
    private $phone;        // Optional
    private $address;      // Optional

    public function __construct($email, $cellPhone) {
        $this->email = $email;
        $this->cellphone = $cellphone;
    }

    /** Here goes a bunch of setter getter for those properties **/
}

如果员工的电话被更改,那么

是不是更好
  

$员工 - > getContactInformation() - > setPhone($ newPhone)

而不是强迫不可变性,如

  

$ employee-> setContactInformation(new ContactInformation(/ ** copy paste   这里** /));

按照Eric Evans的书,我明白当你改变一个值对象的一部分时,它就是一个完全不同的值对象,然后就是它。

我无法得到关于像ContactInformation这样的更复杂的对象的相同结论,它应该是一个实体吗?

需要建议,谢谢!

3 个答案:

答案 0 :(得分:1)

值对象应该是不可变的,但ContactInformation不应该是值对象。

考虑两个共享房屋的人,因此具有相同的地址和电话号码(如果您认为他们没有手机或电子邮件,则会简化问题)。

在这种情况下,他们会有相同的ContactInformation个对象,但联系信息仍然明显是一个人或另一个人。如果他们中的一个搬出,或购买手机,那么他们的联系信息将不得不改变。

正如您在评论中提到的,最好将ContactInformation视为可变实体,而不是值对象。

答案 1 :(得分:1)

对我而言,员工依赖ContactInformation意味着您希望ContactInformation已经设置了电话等,然后传递给员工,即:

$contanctInfo = new ContactInformation();
$contanctInfo->setPhone(820);
$employee1 = new Employee($contanctInfo);
echo $employee1->getContanctInformation()->getPhone(); // 820

但是,这意味着在实例化每个Employee之前,您需要生成尽可能多的ContactInformation对象,如Employee对象,因为:

$contanctInfo = new ContactInformation();
$contanctInfo->setPhone(820);
$employee1 = new Employee($contanctInfo);
$employee2 = new Employee($contanctInfo);
$employee1->getContactInformation()->setPhone(123);
$employee2->getContactInformation()->setPhone(666);

echo $employee1->getContactInformation()->getPhone();
echo $employee2->getContactInformation()->getPhone();

变成:

666
666

因为您正在更改同一个对象。

在您的情况下,当您在实例化ContactInformation时使依赖变得有点无用时,您同时实例化Employee

$employee1 = new Employee(new ContactInformation());
$employee2 = new Employee(new ContactInformation());

$employee1->getContactInformation()->setPhone(123);
$employee2->getContactInformation()->setPhone(666);

echo $employee1->getContactInformation()->getPhone();
echo $employee2->getContactInformation()->getPhone();

结果:

123
666

因此,如果您不希望在将依赖对象注入其他对象之前对其进行更改,则根本不需要依赖对象。更重要的是,在现实世界中ContanctInformationEmployee绑定,而不是相反。

我会这样做:

class Employee {

    private $_contactInformation;

    public function __construct() {
        $this->_contactInformation = new ContactInformation($this);
    }

    /**
     * @return ContactInformation
     */
    public function getContactInformation() {
        return $this->_contactInformation;
    }
}

class ContactInformation {

    private $_employee;

    private $email;        // Required
    private $cellPhone;    // Required
    private $phone;        // Optional
    private $address;      // Optional

    public function __construct(Employee $employee) {
        $this->_employee = $employee;
    }

    public function setPhone($phone) {
        $this->phone = $phone;
    }

    public function getPhone() {
        return $this->phone;
    }
}

$employee1 = new Employee();
$employee2 = new Employee();

$employee1->getContactInformation()->setPhone(123);
$employee2->getContanctInformation()->setPhone(666);

echo $employee1->getContactInformation()->getPhone();
echo $employee2->getContactInformation()->getPhone();

另一方面,如果员工有一些识别器,那么ContanctInfo会根据ID检索信息,您可以按照自己的方式进行注射。

答案 2 :(得分:1)

有趣的讨论。我所做的是让getContactInformation返回一个克隆。

class Employee {

public function getContactInformation() {
    return clone $this->contactInformation;
}
...
$contactInformation = $employee->getContactInformation();
$contactInformation->setPhone('');
$employee->setContactInformation($contactInformation);

通过这样做,实际存储在Employee中的值对象是不可变的。你总是拉一份副本,因为它不再与员工挂钩,不再是一个价值对象(人们可以争辩说有很多挥手),因此可以修改而不会扰乱DDD警察。

适合我,让我使用一个需要制定者的表格系统。