Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 单一责任原则与贫血领域模型反模式_Oop_Solid Principles_Single Responsibility Principle_Anemic Domain Model - Fatal编程技术网

Oop 单一责任原则与贫血领域模型反模式

Oop 单一责任原则与贫血领域模型反模式,oop,solid-principles,single-responsibility-principle,anemic-domain-model,Oop,Solid Principles,Single Responsibility Principle,Anemic Domain Model,我所从事的项目非常重视单一责任原则。我们有很多小班,事情很简单。然而,我们有一个贫血的领域模型-在我们的任何模型类中都没有行为,它们只是属性包。这并不是对我们的设计的抱怨——它实际上似乎工作得很好 在设计评审期间,每当向系统中添加新行为时,就会出现SRP,因此新行为通常会出现在新类中。这使得事情很容易进行单元测试,但有时我会感到困惑,因为这感觉像是把行为从相关的地方拉出来 我正在努力提高我对如何正确应用SRP的理解。在我看来,SRP反对将共享相同上下文的业务建模行为添加到一个对象,因为该对象最终

我所从事的项目非常重视单一责任原则。我们有很多小班,事情很简单。然而,我们有一个贫血的领域模型-在我们的任何模型类中都没有行为,它们只是属性包。这并不是对我们的设计的抱怨——它实际上似乎工作得很好

在设计评审期间,每当向系统中添加新行为时,就会出现SRP,因此新行为通常会出现在新类中。这使得事情很容易进行单元测试,但有时我会感到困惑,因为这感觉像是把行为从相关的地方拉出来

我正在努力提高我对如何正确应用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);
    }
}