域驱动设计 – DDD:如何保持复杂值对象不可变?
|
我想将Address建模为值对象.因为将它变为不可变是一个好习惯,所以我选择不提供任何可能允许稍后修改它的setter. 一种常见的方法是将数据传递给构造函数;但是,当值对象非常大时,可能会变得非常臃肿: class Address {
public function __construct(
Point $location,$houseNumber,$streetName,$postcode,$poBox,$city,$region,$country) {
// ...
}
}
另一种方法是将参数作为数组提供,从而产生一个干净的构造函数,但这可能会破坏构造函数的实现: class Address {
public function __construct(array $parts) {
if (! isset($parts['location']) || ! $location instanceof Point) {
throw new Exception('The location is required');
}
$this->location = $location;
// ...
if (isset($parts['poBox'])) {
$this->poBox = $parts['poBox'];
}
// ...
}
}
这对我来说也有点不自然. 关于如何正确实现一个非常大的价值对象的任何建议? large list of parameters的主要问题是可读性以及混淆参数的危险.您可以使用0700解决这些问题,如 Effective Java所述.它使代码更具可读性(尤其是不支持命名和可选参数的语言):public class AddressBuilder {
private Point _point;
private String _houseNumber;
// other parameters
public AddressBuilder() {
}
public AddressBuilder WithPoint(Point point) {
_point = point;
return this;
}
public AddressBuilder WithHouseNumber(String houseNumber) {
_houseNumber = houseNumber;
return this;
}
public Address Build() {
return new Address(_point,_houseNumber,...);
}
}
Address address = new AddressBuilder()
.WithHouseNumber("123")
.WithPoint(point)
.Build();
优点: >参数被命名,因此它更具可读性 我能想到的一个缺点是忘记指定其中一个参数(例如,不调用WithHouseNumber)将导致运行时错误,而不是使用构造函数时的编译时错误.您还应该考虑使用更多的Value对象,例如PostalCode(与传递字符串相反). 在相关的说明中,有时业务需求要求更改值对象的一部分.例如,当最初输入地址时,街道号码可能拼写错误,现在需要更正.由于您将Address建模为不可变对象,因此没有setter.解决此问题的一种可能方法是在地址值对象上引入“无副作用函数”.该函数将返回对象本身的副本,但新的街道名称除外: public class Address {
private readonly String _streetName;
private readonly String _houseNumber;
...
public Address WithNewStreetName(String newStreetName) {
// enforce street name rules (not null,format etc)
return new Address(
newStreetName
// copy other members from this instance
_houseNumber);
}
...
} (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
