单一责任和依赖关系
本文关键字:关系 依赖 责任 单一 | 更新日期: 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(){}…
}
这个依赖关系没有意义。
虽然它没有强耦合Person
和Storage
,因为它没有将它们绑定到特定的存储实现,但我认为任何这样的依赖都没有意义。
方法作为动词
将类上的方法看作由该类型执行的动词。你告诉该类型的实例"做某事",相对于它的局部域。
当我作为一个人,Save
是什么意思?
- 我换了保险公司,我的费用减少了15%? 我是一个救赎的神?我已经把我的灵魂下载到一个机器里了?
存储业务可以且应该Save
。人们不能Save
,也不应该宣传他们可以。
SaveTo
可能更有意义-即public void SaveTo(IStorageService storage)
。
但是你是说一个人有责任知道如何保存自己。在我看来,这违反了SRP。它还显示了域分析的缺失部分。
Person
的域不包含任何关于保存、存储等的内容。它将包含人与人之间的交互,以及该领域级别上的其他事物。数据持久性领域更适合Save
方法。
如果Person
在问题域中(在那个抽象级别),那么Storage
在解决方案域中。
如何分离逻辑
这里有三段逻辑:
-
Person
-知道"人的事情" -
Storage
-知道特定类型的存储,以及如何访问它 -
Storage of Person
-知道一个人应该如何提交到存储
按照我上面的建议,我会让Person
独立存在。但是,您可以将Storage
和Storage of Person
的逻辑分开,也可以将它们组合在一起。
orm采用的方法是将这三个概念分开。"对象关系映射"中的"映射"是封装"人的存储"的地方。
这种方法允许您的Storage
逻辑专注于读取存储配置,连接到存储,确保存储速度快,选择备用存储方法等潜在复杂的工作。它还消除了对主域模型的任何依赖,因此存储代码可以被任何其他域模型重用。
我会坚持使用第二个版本。如果它只有一个职责,你可以使用第一个版本。但是,在我看来,我喜欢将持久层与模型对象分开。
另外,您可以序列化第二个版本,这可能会很有帮助。您可能无法使用对IStorageService的引用来序列化第一个版本。
如果您仔细阅读SRP的定义,您会注意到职责的定义是更改的理由。
第一个版本可能有两个改变的原因:
- 持久化API更改
- 其值的形状变化
我发现您的Domain名称空间越完整和经过测试,您的应用程序的质量就越好。持久化实体不属于域模型,所以我把它们分开了。
如果person是您正在建模的域对象,我不建议将其封装为服务。我的意思是,我不知道你,但我是一个人,我没有存储服务;)
我想说,对贫血域模型的关注是对域对象如何相互关联的关注。如果它们仅仅是字面意思的财产袋,那是贫血的。你可能不希望通过让他们关心建模的事情,以及弄清楚何时以及如何持久化自己来解决这个问题。
贫血域的考虑可以通过丰富的相互作用来弥补。也许您正在为一所学校建模,Person是一个对象,可以是Student或Teacher, Class是学生和老师的集合。这样你就有了myTeacher的概念。布置(家庭作业,课堂)或类似的事情。这就是我个人丰富我的领域模型的方式——通过在你的领域实体之间建模实际的、概念性的交互,而不是通过"建模"它们如何与你的数据访问管道代码交互。
这只是我的两分钱。根据单一职责原则,一个类应该只有一个改变的原因。更改的原因或来源是我们为不同的业务部门创建和设计不同的类。
所以要知道是否有SRP违反,设计师需要知道他工作的不同业务部门,并设计类,这样没有一个类具有多个部门的行为或状态。这可能因企业而异。
虽然在您给出的第一个示例中,很明显可以有两个更改源,即客户管理器(用于管理客户姓名,出生日期)和数据库管理员/模式设计器(将客户保存到数据库)。因此,它显然违反了SRP。
在第二个示例中,您在两个不同的类中管理两个不同的更改源。第二个例子是正确的。虽然我会更改类名:)。