Domain driven design DDD和getter和setter的使用
我已经阅读了一些关于getter和setter的使用,以及它们如何帮助克服域模型对象中封装的目的的文章/帖子。我理解不使用setter背后的逻辑——您允许客户机代码在对象业务规则和不变量的上下文之外操作该对象的属性 现在这个原则仍然让我困惑。例如,如果需要更改对象的成员变量的值,会发生什么情况?例如,如果一个人的名字发生了变化,我如何在模型中反映这一点?起初我想,为什么不使用一个名为“ChangeName”的函数,让我传入新名称,然后它可以更改内部“name”变量。好。。。。那只是个二传手,不是吗 我需要澄清的是——如果我要完全消除setter,那么在上述情况下,我应该仅仅依赖构造函数参数吗?我是否应该通过构造函数传递新的属性值来代替旧的属性值,然后我可以通过将对象传递到我拥有的任何持久性基础结构来持久化更改 这两篇文章在本次讨论中很有用:Domain driven design DDD和getter和setter的使用,domain-driven-design,encapsulation,setter,getter,getter-setter,Domain Driven Design,Encapsulation,Setter,Getter,Getter Setter,我已经阅读了一些关于getter和setter的使用,以及它们如何帮助克服域模型对象中封装的目的的文章/帖子。我理解不使用setter背后的逻辑——您允许客户机代码在对象业务规则和不变量的上下文之外操作该对象的属性 现在这个原则仍然让我困惑。例如,如果需要更改对象的成员变量的值,会发生什么情况?例如,如果一个人的名字发生了变化,我如何在模型中反映这一点?起初我想,为什么不使用一个名为“ChangeName”的函数,让我传入新名称,然后它可以更改内部“name”变量。好。。。。那只是个二传手,不是
这是一个经典的讨论。堆栈溢出中还有其他几个线程 但是。获取/设置(自动属性?)并不都是坏的。但它们往往使您将实体构造为“死”数据容器,只包含prop而不包含方法。这种症状通常被称为贫血区,并且很少有行为。我的建议是:
关于在构造函数中设置数据:我只在该实体没有该数据无法“生存”/“存在”时才这样做。对于实体人来说,我想说这个名字可能并不那么重要。但社会保险号可能是构造器数据的候选。或者实体员工必须在构造函数中有公司,因为员工必须属于公司。我认为我们应该看看DDD的原则,并从中得出正确的答案 C#中的公共自动属性getter/setter在功能上只是公共属性。使用自动属性getter/setter本身并不坏,只要没有关于各个属性的正确值的业务规则,并且在这些属性更改时没有需要触发的域事件 此外,不应构建仅具有公共汽车属性的集合或实体,因为这会导致贫血模型和贫血领域。这样的“聚合”不是实际的聚合,而是一个DTO或值对象 就我个人而言,我认为如果我们使用属性访问器(get/set)和实体来集成业务逻辑,我们可以使代码更具可读性,并且可能不会太冗长 例如:
//而不是这个:
公共人口总数:IAggregate
{
公共字符串名称{get;private set;}
public void ChangeName(字符串newName)
{
Name=Check.MinMaxLength(newName,1100,
$“{nameof(newName)}长度必须介于1到100个字符之间。”);
}
}
/*MinMaxLength抛出一个业务异常
*如果新名称超出可接受的范围
*否则,它将返回不变的值。
*/
//…您可以编写以下内容以减少一种方法:
公共AltDemoAggregate:IAggregate
{
私有字符串\u名称;
公共字符串名
{
get=>\u name;
set=>value=Check.MinMaxLength(newName,1100,
$“{nameof(newName)}长度必须介于1到100个字符之间。”);
}
}
上面提到的唯一问题是,在内部,如果用某种方法直接设置\u name
,则可以绕过业务逻辑。但如果你有足够的纪律,我认为这不是问题。但对某些人来说,这似乎很可怕,我理解
另一方面,如果您使用的是实体框架之类的东西,我认为您可以通过调用属性(而不是备份字段)将其配置为水合新实例,从而防止从数据库加载无效聚合(例如,如果您导入了一些可能包含垃圾的批量数据)。我还没有测试过这个
在第二个示例中使用表达式体访问器只是为了表明可以大大减少样板文件
可以使用上面这样的表达式体getter,因为字符串在C中具有值语义,所以表达式返回\u name
的副本,从而不公开对内部变量的引用
请注意,例如,对于C#9记录,您只有基于值的相等语义。记录仍通过引用传递!由于记录在理想情况下应该是不可变的(仅限init),因此可以返回对此类记录的引用(这更有效),而不必进行克隆(这对于浅克隆很简单,但对于深克隆很难)
如果在聚合中有这样一个对象,例如DDD值对象不是不可变的记录或可以轻松克隆的记录,则需要确保没有返回对内部对象的引用,而该对象可能会发生变化,从而绕过业务逻辑和梅森