Oop 单一责任原则与贫血领域模型反模式
我所从事的项目非常重视单一责任原则。我们有很多小班,事情很简单。然而,我们有一个贫血的领域模型-在我们的任何模型类中都没有行为,它们只是属性包。这并不是对我们的设计的抱怨——它实际上似乎工作得很好 在设计评审期间,每当向系统中添加新行为时,就会出现SRP,因此新行为通常会出现在新类中。这使得事情很容易进行单元测试,但有时我会感到困惑,因为这感觉像是把行为从相关的地方拉出来 我正在努力提高我对如何正确应用SRP的理解。在我看来,SRP反对将共享相同上下文的业务建模行为添加到一个对象,因为该对象最终不可避免地会做多个相关的事情,或者做一件事情但知道多个业务规则,从而改变其输出的形状 如果是这样,那么最终的结果就是一个贫血的领域模型,这在我们的项目中是肯定的。然而贫血域模型是一种反模式 这两种想法能共存吗 编辑:两个与上下文相关的链接: SRP-Oop 单一责任原则与贫血领域模型反模式,oop,solid-principles,single-responsibility-principle,anemic-domain-model,Oop,Solid Principles,Single Responsibility Principle,Anemic Domain Model,我所从事的项目非常重视单一责任原则。我们有很多小班,事情很简单。然而,我们有一个贫血的领域模型-在我们的任何模型类中都没有行为,它们只是属性包。这并不是对我们的设计的抱怨——它实际上似乎工作得很好 在设计评审期间,每当向系统中添加新行为时,就会出现SRP,因此新行为通常会出现在新类中。这使得事情很容易进行单元测试,但有时我会感到困惑,因为这感觉像是把行为从相关的地方拉出来 我正在努力提高我对如何正确应用SRP的理解。在我看来,SRP反对将共享相同上下文的业务建模行为添加到一个对象,因为该对象最终
贫血区域模型- 我不是那种只喜欢寻找先知并遵循他们所说的福音的开发者。因此,我不提供这些的链接,作为说明“这些是规则”的一种方式,只是作为这两个概念的定义来源。我不得不说“是”,但你必须正确地执行SRP。如果同一个操作只适用于一个类,那么它就属于那个类,你说呢?如果同一个操作适用于多个类呢?在这种情况下,如果您想遵循组合数据和行为的OO模型,您应该将操作放入基类中,不是吗 我怀疑从你的描述中,你最终得到的类基本上是一堆操作,因此你基本上重新创建了C风格的编码:结构和模块 从链接的SRP文件中:
“SRP是最简单的原则之一,也是最难纠正的原则之一。”我发现遵循坚实的原则确实会让我远离DDD的富域模型,最后,我发现我并不在乎。更重要的是,我发现域模型的逻辑概念和任何语言的类都不是1:1映射的,除非我们讨论的是某种外观 我不会说这完全是一种c风格的编程,其中有结构和模块,但最终可能会得到更具功能性的东西,我意识到这些风格是相似的,但细节有很大的不同。我发现我的类实例的行为最终类似于高阶函数、部分函数应用程序、延迟求值函数或上述函数的某种组合。这对我来说有点难以形容,但这是我从遵循TDD+SOLID编写代码中得到的感觉,它最终表现为一种混合的OO/函数式风格
至于继承是一个不好的词,我认为这更多地是因为继承在Java/C这样的语言中不够细粒度。在其他语言中,这不是一个问题,而是更有用。我喜欢SRP的定义为: “一个类只有一个要更改的业务原因” 因此,只要行为可以归为单一的“商业原因”,那么它们就没有理由不在同一类中共存。当然,“业务原因”的定义还有待商榷(所有利益相关者都应该商榷)。丰富领域模型(RDM)和单一责任原则(SRP)并不一定存在分歧。RDM与SRP的一个非常专业的子类——提倡“数据bean+控制器类中的所有业务逻辑”(DBABLICC)的模型——的矛盾更大 如果您阅读Martin的,您将看到他的调制解调器示例完全在域层中,但将数据通道和连接概念抽象为单独的类。他将调制解调器本身作为包装器,因为这对客户机代码是有用的抽象。与其说是分层,还不如说是适当的(重新)分解。内聚和耦合仍然是设计的基本原则 最后,有三个问题:
- 正如马丁自己所指出的,看到不同的“改变的原因”并不总是容易的。雅格尼、敏捷等概念本身就阻碍了对未来变化原因的预期,因此我们不应该在它们不明显的地方发明它们。我认为“过早的、预期的变更原因”是应用SRP的真正风险,应该由开发人员管理
- 更进一步,即使正确(但不必要的分析)应用SRP也可能导致不必要的复杂性。总是想想下一个必须维护你的类的可怜的sod:将琐碎的行为勤奋地抽象到它自己的接口、基类和一行实现中真的有助于他理解什么应该只是一个类吗
- 软件设计通常是关于在竞争力量之间取得最佳折衷。例如,分层体系结构通常是SRP的一个很好的应用程序,但是,例如,将业务类的属性从布尔值更改为枚举会对所有层产生连锁反应,从db到域、外观、web服务,再到GUI?这是否意味着糟糕的设计?不一定:它指出了这样一个事实,即您的设计倾向于从一个方面改变到另一个方面
public class Object1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
private IAction1 action1;
public Object1(IAction1 action1)
{
this.action1 = action1;
}
public void DoAction1()
{
action1.Do(Property1);
}
}
public interface IAction1
{
void Do(string input1);
}
public class Object1Service
{
private Object1Repository repository;
public Object1Service(Object1Repository repository)
{
this.repository = repository;
}
// Tie in your Unit of Work Aspect'ing stuff or whatever if need be
public void DoAction1(Object1DTO object1DTO)
{
Object1 object1 = repository.GetById(object1DTO.Id);
object1.DoAction1();
repository.Save(object1);
}
}