单一责任和依赖关系

本文关键字:关系 依赖 责任 单一 | 更新日期: 2023-09-27 18:14:08

如果一个对象有一个单一的职责,可以接受以下内容:

public class Person
{
   public string Name;
   public DateTime DateOfBirth;
   private IStorageService _storageService;
   public Person(IStorageService storageService)
   {
      _storageService = storageService
   }
   public void Save()
   {
        _storageService.Persist(this);
   }
}

。使用提供的合作者(这也有助于阻止领域模型贫血)。

还是:

public class Person
{
   public string Name;
   public DateTime DateOfBirth;
   public Person()
   {
   }

}
public class StorageService
{
    public void Persist(Person p)
    {
    }
}

单一责任和依赖关系

以下内容可以接受吗?

类Person {
Person(IStorageService){}…
void Save(){}…
}

这个依赖关系没有意义。

虽然它没有强耦合PersonStorage,因为它没有将它们绑定到特定的存储实现,但我认为任何这样的依赖都没有意义。

方法作为动词

将类上的方法看作由该类型执行的动词。你告诉该类型的实例"做某事",相对于它的局部域。

当我作为一个人,Save是什么意思?

  • 我换了保险公司,我的费用减少了15%?
  • 我是一个救赎的神?我已经把我的灵魂下载到一个机器里了?

存储业务可以且应该Save。人们不能Save,也不应该宣传他们可以。

SaveTo可能更有意义-即public void SaveTo(IStorageService storage)

但是你是说一个人有责任知道如何保存自己。在我看来,这违反了SRP。它还显示了域分析的缺失部分。

Person的域不包含任何关于保存、存储等的内容。它将包含人与人之间的交互,以及该领域级别上的其他事物。数据持久性领域更适合Save方法。

如果Person在问题域中(在那个抽象级别),那么Storage在解决方案域中。

如何分离逻辑

这里有三段逻辑:

  1. Person -知道"人的事情"
  2. Storage -知道特定类型的存储,以及如何访问它
  3. Storage of Person -知道一个人应该如何提交到存储

按照我上面的建议,我会让Person独立存在。但是,您可以将StorageStorage of Person的逻辑分开,也可以将它们组合在一起。

orm采用的方法是将这三个概念分开。"对象关系映射"中的"映射"是封装"人的存储"的地方。

这种方法允许您的Storage逻辑专注于读取存储配置,连接到存储,确保存储速度快,选择备用存储方法等潜在复杂的工作。它还消除了对主域模型的任何依赖,因此存储代码可以被任何其他域模型重用。

我会坚持使用第二个版本。如果它只有一个职责,你可以使用第一个版本。但是,在我看来,我喜欢将持久层与模型对象分开。

另外,您可以序列化第二个版本,这可能会很有帮助。您可能无法使用对IStorageService的引用来序列化第一个版本。

如果您仔细阅读SRP的定义,您会注意到职责的定义是更改的理由。

第一个版本可能有两个改变的原因:

  • 持久化API更改
  • 其值的形状变化
因此,它不遵循SRP,而第二个版本遵循SRP。

我发现您的Domain名称空间越完整和经过测试,您的应用程序的质量就越好。持久化实体不属于域模型,所以我把它们分开了。

如果person是您正在建模的域对象,我不建议将其封装为服务。我的意思是,我不知道你,但我是一个人,我没有存储服务;)

我想说,对贫血域模型的关注是对域对象如何相互关联的关注。如果它们仅仅是字面意思的财产袋,那是贫血的。你可能不希望通过让他们关心建模的事情,以及弄清楚何时以及如何持久化自己来解决这个问题。

贫血域的考虑可以通过丰富的相互作用来弥补。也许您正在为一所学校建模,Person是一个对象,可以是Student或Teacher, Class是学生和老师的集合。这样你就有了myTeacher的概念。布置(家庭作业,课堂)或类似的事情。这就是我个人丰富我的领域模型的方式——通过在你的领域实体之间建模实际的、概念性的交互,而不是通过"建模"它们如何与你的数据访问管道代码交互。

这只是我的两分钱。

根据单一职责原则,一个类应该只有一个改变的原因。更改的原因或来源是我们为不同的业务部门创建和设计不同的类。

所以要知道是否有SRP违反,设计师需要知道他工作的不同业务部门,并设计类,这样没有一个类具有多个部门的行为或状态。这可能因企业而异。

虽然在您给出的第一个示例中,很明显可以有两个更改源,即客户管理器(用于管理客户姓名,出生日期)和数据库管理员/模式设计器(将客户保存到数据库)。因此,它显然违反了SRP。

在第二个示例中,您在两个不同的类中管理两个不同的更改源。第二个例子是正确的。虽然我会更改类名:)。