我正在尽力创建一个web应用程序,以熟悉DDD和ValueObjects。到目前为止,一切都很好,我只是在问自己,我是否正确地实现了我的价值对象。以下是我的当前状态(从非必要部分剥离代码):
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类中吗?
非常感谢您的帮助!
$user->credentials = $user->credentials->changePassword("newPassword");
这是正确的想法。
在这个实现中,$username和$password是Credentials状态的一部分。你希望这种状态是不可变的;构造函数完成后,不应该有任何代码更改该状态。
然而,你需要以某种方式暴露这种状态;不可变的只写对象不会提供太多的业务价值。将值类型实现为不可变公共属性的集合是一种常见的习惯用法。这里有一个关于如何在PHP中做到这一点的老讨论。或者,您可以实现允许Credentials对象将该状态的副本传递给其他对象的调用。
我应该为User类的$credentials属性创建一个getter和一个setter吗?
通常情况下——User的实现有一个Id属性,这使它看起来像一个实体。实体很少允许其内部状态逃脱。特别是,setter是许多9,这从来都不是一个好主意——实体的意义在于它们可以强制执行自己的数据约束;他们负责确保对其状态的所有更改都满足业务不变量。
提供对不可变状态的访问的getter不太可能引起问题;允许调用者导航到可变状态的getter令人担忧。
但是:getter并不是特别有表现力——可能由域服务支持的查询通常是一个更灵活的选择
比较:
password = user.password
strength = calulatatePasswordStrength(password.toString)
至
password = user.password
strength = password.strength(calculator)
至
strength = user.passwordStrength(calculator)
我应该把changePassword()方法放在User类中吗?
假设您支持该用例,是的。Credentials值知道如何从旧证书计算新的证书状态;用户实体知道如何验证允许处于其当前状态的用户以这种方式更改凭证。
(例如:一个简单的安全实现可能会跟踪凭据上次更改的时间,并具有限制密码更改的策略,其中要使用的正确策略取决于用户的其他属性。这对凭据对象来说太多了,无法单独完成。)
特别是,用户的密码实际上是Credentials值对象中的一组状态,这是一个实现细节,不应该向调用user的代码公开。换句话说,如果User正在实现接口IUser(隔离实现细节),那么我希望IUser承诺将提供changePassword($password)方法。
我想差不多了。值对象的关键是它是一个值。也就是说,你可以将它与另一个相同类型的对象进行比较,并根据它的值来确定它是否不同。在这种情况下,我希望能够看到用户名=x和密码=x的凭据将"等于"另一个具有相同值的凭据。
这并不一定意味着你在球场上需要getter。
在我的博客上,我有一个关于这个主题的有趣的代码卡塔。你可以在这里找到为什么私有C#变量没有你想象的那么私有
值对象kata位于底部附近。希望能有所帮助。