单一责任和依赖

时间:2011-11-11 21:09:20

标签: c# oop dependency-injection single-responsibility-principle

如果某个对象有Single Responsibility,则可以接受以下内容:

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)
    {
    }
}

6 个答案:

答案 0 :(得分:10)

  

可以接受以下内容吗?

     

班级人员(
  人(IStorageService){} ...
  void Save(){} ...
  }

这种依赖没有意义。

虽然它没有将Person强烈耦合到Storage,因为它没有将它们绑定到特定的存储实现,我认为任何此类依赖都会使毫无意义。

作为动词的方法

将类上的方法视为将由该类型执行的动词。您告诉该类型的实例“做某事”,就其本地域而言。

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

  • 我改变了我的保险提供商并将成本降低了15%?
  • 我是一个救赎神?
  • 我已经将自己的灵魂下载到自动机中了?

存储服务可以而且应该Save。人们不能Save,也不应该宣传他们可以。{/ p>

尝试在

中穿上它

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

但是,你说的是一个人有责任知道如何将自己保存到存储中。在我看来,这是a violation of SRP。它还显示a missing piece of Domain Analysis

Person的域名不包含任何有关保存,存储等的内容。它将包含人员之间的互动以及该域级别的其他内容。数据持久性领域是Save方法的更好地方。

如果Person位于问题域中(在该抽象级别),则Storage位于解决方案域中。

如何区分逻辑

这里有三条逻辑:

  1. Person - 了解“人物”
  2. Storage - 了解特定类型的存储以及如何访问它
  3. Storage of Person - 了解一个人应该如何致力于存储
  4. 根据我的建议,我会让Person独立。但是,您可以将StorageStorage of Person的逻辑分开,也可以将它们合并。

    The approach that ORMs take是将所有三个概念分开。 “对象关系映射”中的“映射”是封装“人员存储”的地方。

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

答案 1 :(得分:4)

我会坚持使用第二个版本。如果它有单一责任,您可以使用第一个版本。但是,在我看来,我喜欢考虑与模型对象分离的持久层。

此外,您可以序列化第二个版本,这可能会有所帮助。您可能无法通过引用IStorageService来序列化第一个版本。

答案 2 :(得分:4)

如果您仔细阅读SRP的定义,您会发现责任的定义是更改原因

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

  • 持久性API更改
  • 其值的形状发生变化

因此,它不会遵循SRP,而第二个版本会这样做。

答案 3 :(得分:0)

我发现你的Domain命名空间越完整和经过测试,你的应用程序的质量就越好。持久化实体不属于域模型,因此我将两者分开。

答案 4 :(得分:0)

如果person是您正在建模的域对象,我不会主张让它封装服务。我的意思是,我不了解你,但我是一个人,我没有存储服务;)

我认为对贫血领域模型的担忧是关于域对象如何相互关联的问题。如果他们只是文字的财产袋,那就是贫血。你可能不想通过让他们关心建模事物以及弄清楚何时以及如何坚持自己来解决这个问题。

贫血领域的考虑将通过丰富的互动来弥补。也许你是在为一所学校建模,而人是一个可以是学生或教师的对象,而班级是学生和教师的集合。然后你会有一些myTeacher.Assign(Homework hw,Class class)或类似的概念。这就是我个人如何丰富我的域模型 - 通过模拟域实体之间的实际概念交互,而不是“建模”它们如何与您的数据访问管道代码交互。

只是我的两分钱。

答案 5 :(得分:0)

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

因此,要知道是否存在SRP违规,设计人员需要了解他所服务的不同业务部门,并设计类,以便没有类具有多个部门的行为或状态。这可能因企业而异。

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

在第二个例子中,你在两个不同的类中管理两个不同的变化来源。所以,我想说第二个例子是正确的。虽然我会更改课程名称:)。