【问题标题】:DDD encapsulation and the repository patternDDD 封装和存储库模式
【发布时间】:2012-08-03 13:06:56
【问题描述】:

假设我有这个简单的类

public class MyEntity
{
    public DateTime DateUpdated { get; private set; }
    public string Author { get; private set; }
    public string Comment { get; private set; }

    public void AddComment(string comment, string author)
    {
        Author = author;
        Comment = comment;
        DateUpdated = DateTime.Now;
    }
}

我已将 setter 设为私有以封装类,并添加了 AddComment 方法以向我的类添加一些行为。这在创建新对象时工作得非常好,但是当我想从数据库加载实体时,DateUpdated 当然设置为我想避免的当前日期。

是否有任何模式可以用来避免公开 DateUpdated 设置器,因为这似乎破坏了我的良好封装并弄乱了类的干净接口?当然,这个类只是一个更一般的问题的例子。

在不创建更多公共构造函数的情况下,我现在最接近的是创建一个私有构造函数,我通过公共静态方法访问它。

【问题讨论】:

  • 为什么 DateUpdated 的 setter 是私有的?恕我直言,实体不必为所有事情都有私人二传手。对于值类型,这当然是另一回事。
  • 这只是任何只读属性的一个例子,只能通过我的特定方法进行更改。
  • 只是好奇,你最终为此做了什么?我遇到了同样的问题。

标签: c# domain-driven-design


【解决方案1】:

使用一个构造函数,它接受与对象字段匹配的参数。

这将允许您在启动时填充对象并保持它们不可变。

public MyEntity(DateTime dateUpdated, string author, string comment)
{
  DateUpdated = dateUpdated;
  Author = author;
  Comment = comment;
}

【讨论】:

  • 如果我可以使该构造函数仅在存储库中可用,因为关键是隐藏此类的其他客户端对 DateUpdated 字段的写访问权限。我知道我在这里吹毛求疵,但它更多的是封装和 DDD 的原则,而不是实际用例。
  • @TT。 - 有了这个你只能“写”到字段一次(特别是如果你把它设为readonly)。这可确保它仅由 选择的值填充。
  • 我并不关心对象的不变性,而是我向这个类的客户公开的类接口
【解决方案2】:

查看 Memento 模式,为您的对象补充水分。构造函数仅用于创建新实例。

【讨论】:

    【解决方案3】:

    你可以像这样重载 AddComment 方法:

    public class MyEntity
    {
        public DateTime DateUpdated { get; private set; }
        public string Author { get; private set; }
        public string Comment { get; private set; }
    
        public void AddComment(string comment, string author)
        {
            Author = author;
            Comment = comment;
            DateUpdated = DateTime.Now;
        }
    
        public void AddComment(string comment, string author, DateTime dateUpdated)
        {
            Author = author;
            Comment = comment;
            DateUpdated = dateUpdated;
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果您使用诸如 NHibernate 之类的 ORM 来实现存储库,那么即使属性是私有集,它也会根据数据库中的数据为属性分配值。也就是说,它绕过AddComment方法,直接注入数据。这是有道理的,因为在重构实体时,行为不会重复,只需要复制数据。 NHibernate 确实要求实体包含受保护的无参数构造函数。如果使用您自己的 ORM 实现,那么您可以使用 Oded 建议的构造函数模式,因为在这种情况下,您的实体可以真正保持对持久性的无知。

      【讨论】:

      • 有趣。您知道 NHibernate 是否以任何方式子类化您的实体以使用受保护的构造函数?
      【解决方案5】:

      如果负责创建这些对象的存储库位于同一个程序集中,您应该查看internal access modifier。如果这符合您项目的需求,您可以通过以下两种方式之一来实现它...

      1. 将您的二传手从 private 更改为 internal。然后,创建者只需在实例化后设置属性值。
      2. 添加一个internal 构造函数,该构造函数接受所有属性的值并设置它们。

      无论哪种方式,您仍然可以通过您在示例中演示的公共方法进行更改。

      【讨论】:

        猜你喜欢
        • 2010-11-11
        • 2017-08-07
        • 2010-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-09
        • 1970-01-01
        相关资源
        最近更新 更多