我想就如何避免编写仅仅是数据容器的对象提出一些建议。
考虑以下聚合根:
public class Post : IAggregateRoot
{
List<Comment> Comments {get; set;}
}
鉴于管理聚合根的工作原理,是否可以像这样调用上面的代码?
new Post().Comments.Add(New Comment("stuff"));
或者这是正确的方法吗?
public class Post : IAggregateRoot
{
List<Comment> Comments {get; private set;}
public void AddComment(string message)
{
Comments.Add(new Comment(message));
}
}
并且这样称呼:
new Post().AddComment("stuff");
这是Eric Evan意味着Aggregate Roots是原子的吗?
如果是这种情况,是否意味着实体没有任何公共设置者,而是具有支持方法(AddThis,RemoveThat)?这是你如何创建具有丰富行为的对象吗?
答案 0 :(得分:5)
你已经有了聚合根的概念,但你的两个选择实际上是关于实现 - 两者都是有效的。
选项1
优点:您的实体界面仍然存在 很干净。
缺点:Add
方法需要逻辑
连线之间的关系
Post
和Comment
(想想
NHibernate的)。你可以创建一个
强类型集合和
覆盖Add方法,或者你可以
将事件提升回Post
处理
选项2
优点:Add/Remove
方法为布线逻辑提供了方便的位置。
缺点:随着集合属性数量的增加,您可能会遇到Add/Remove
个方法。此外,公开的集合必须是ReadOnly,以确保始终使用特殊方法添加/删除Comments
。
我的偏好是选项1 - 我使用引发事件的泛型集合。恕我直言,感觉更自然,其他开发人员更容易编码。虽然SO上的其他人表示不同意。
当我们谈论行为时,我们谈论的是将逻辑附加到实体。例如。如果您想在5天后停止Comments
添加,则会询问Post
添加Comment
是否有效,而Post
将包含执行此操作的逻辑检查。
答案 1 :(得分:3)
我会选择第二种选择。
首先,我喜欢将集合显示为IEnumerable。这样就不可能如此轻松地操纵该列表,并且可以防止不需要的行为。我定期检查添加和删除方法,如果对象包含在列表中或不包含。
其次它封装了,之后我可以添加一些逻辑。
最后,您可以通过返回自身来使用该方法进行方法链接:
var post = new Post()
.AddComment("My First comment")
.AddComment("My Second comment")
.Publish();
如果AddX方法对您的实体造成太多膨胀,那么可以通过重载来实现:
var post = new Post()
.Add(new Comment("My First comment"))
.Add(new Comment("My Second comment"))
.Publish();