2016-06-08 35 views
0

我正在尽我所能来创建一个Web应用程序来熟悉DDD和ValueObjects。到目前为止,一切工作正常,我只是问自己,如果我正确实施我的价值对象。这是我目前的状态(代码非必要的部分剥离):ddd - 我的ValueObject实现是否正确?

class User { 

    private $id; 

    /** 
    * @var Credentials 
    */ 
    public $credentials; 

    public function getId() { return $this->id; } 

} 

class Credentials { 

    private $username; 
    private $password; 

    public function __construct($username, $password) { // no need to detail } 

    public function changePassword($newPassword) { 
     return new self($this->username, $newPassword); 
    } 
} 

所以每当我想更新我的用户的密码,我要做$user->credentials = $user->credentials->changePassword("newPassword");

这是正确的吗?我应该为我的User类的$credentials属性创建一个getter和setter?我应该将changePassword()方法放在User类中吗?

任何帮助,非常感谢!

回答

3
$user->credentials = $user->credentials->changePassword("newPassword"); 

这是正确的想法。

在这个实现中,$ username和$ password是Credentials状态的一部分。你希望这个状态是不变的;应该没有代码在构造函数完成后改变该状态。

但是,您需要以某种方式公开该状态;不可变的只写对象不提供非常多的商业价值。将值类型实现为不可变公共属性集合是一种常见的习惯用法。 Here's关于如何在PHP中执行此操作的旧讨论。或者,您可以实施调用,以允许凭证对象将该状态的副本传递给其他对象。

我应该为我的User类的$ credentials属性创建一个getter和setter?

通常不会 - 您的用户实现具有Id属性,这使得它看起来像一个实体。实体很少让他们的内部状态逃脱。特别是,setters有很多九个接近绝不是一个好主意 - 实体的一点是他们可以强制自己的数据约束;他们有责任确保对其状态的所有更改满足业务不变。

提供访问不可变状态的getter不可能导致问题;允许呼叫者导航到可变状态的吸气剂令人担忧。

但是:干将不是特别表现 - 查询,可能是由域服务支持,往往是一种更灵活的选择

比较:

password = user.password 
strength = calulatatePasswordStrength(password.toString) 

password = user.password 
strength = password.strength(calculator) 

strength = user.passwordStrength(calculator) 

我应该把changePassword()方法放在User类中吗?

假设您支持该用例,是的。凭证值知道如何计算旧凭证的新状态;用户实体知道如何验证以当前状态的用户被允许以这种方式更改凭证。 (例如:一个天真的安全实现可能会跟踪上次更改凭证的时间,并且具有限制密码更改的策略,其中使用的正确策略取决于用户的其他属性。对于凭证来说,这样做太多了。对象自己做。)

特别是,用户的密码实际上是Credentials值对象中的一堆状态的事实是一个实现细节,它不应该暴露给调用User的代码。换句话说,如果用户正在实现接口IUser(隔离实现细节),那么我希望IUser承诺changePassword($ password)方法将可用。

+0

好吧,现在看来我更清楚了。我将在我的User类中创建一个changePassword()方法,然后使该属性保持私有状态,这将使代码更加美观,易于阅读。非常感谢 ! – Lucio

0

差不多我会说。值对象的关键是它是一个值。即您可以将其与另一个相同类型的对象进行比较,并可以根据其值确定它是否不同。在这种情况下,我希望能够看到其中username = x和password = x的Credential将'等于'具有相同值的另一个凭证。

这并不一定意味着你需要在领域的getters。

我在我的博客上有这个主题的有趣代码kata。你可以在这里找到它Why Private C# Variables are Not as Private as you Thought

值对象kata接近底部。希望有所帮助。

+0

我看看你的卡塔,没有问题。事实是,当一个ValueObject在一个实体内部时,我需要访问它。我有习惯为所有事情创建getter/setter,并且我试图失去它。然而,执行'$ user-> changePassword($ password)'似乎比'$ user-> credentials = $ user-> credentials-> changePassword($ password)'容易# – Lucio

+0

您是否需要凭证对象所有在这种情况下?它会有更多的功能吗? – Codescribler

+0

其实我不认为,现在其实不是。我以为我会放一个'$ email'属性,但它不适合它,因为它更像是我的项目中的“属性”属性。 – Lucio