【问题标题】:anemic data model, dao's, ... authoritative reference?贫血的数据模型,dao的,……权威参考?
【发布时间】:2013-10-31 11:38:59
【问题描述】:

虽然是一位经验丰富的程序员和架构师,但同样的老基本问题会反复出现。我对此有自己的信仰,但我需要一些权威来源。

贫乏的数据模型((c)Martin Fowler?)天生就不好吗?蛋糕应该可以自己烤吗?发票是否应该知道如何(以及何时应该允许)向自己添加行,还是应该由另一层来做? rabbit.addToHole(hole) 还是hole.addRabbit(rabbit)?是否已经证明 ADM 更容易出错,或者更容易维护,或者其他什么?

你可以在网上找到很多说法,但如果可能的话,我真的想要一些权威的引述、参考资料或事实。

【问题讨论】:

    标签: oop architecture


    【解决方案1】:

    我认为this question 的公认答案也是对您问题的最佳回答。

    我认为必须记住的事情:

    • ADM 对于 CRUD 应用程序来说已经足够了,而且由于大多数应用程序都是以这种方式启动的,因此它可以作为启动架构;如果需要,您可以通过重构从那里发展,但从一开始就过度设计应用程序没有意义
    • 一旦复杂性开始增长 - 一旦业务规则开始堆积 - 保持模型贫乏就不太方便 - 将规则与它们作用的对象分开会使您在查看对象时很难记住所有适用的规则
    • 如果规则在域对象中,它们也有利于编写测试,如果它们在其他地方(比如在无状态服务中),你不知道域对象可以做什么以及适用的所有约束是的,为它编写适当的测试(想想在不同服务中建模的正交规则)
    • 在真正简单的应用程序和贫血的领域模型之间有一个区别:在真正简单的应用程序中,没有太多的业务逻辑,在贫血的领域模型中,逻辑存在,但与领域模型分开李>

    【讨论】:

    • 仍在寻找学术或权威参考。
    【解决方案2】:

    请参阅此stackoverflow answer 以获得启发。

    这是我的看法:

    ADM(贫血领域模型)不能用类图 UML 表示

    贫血域模型不好,仅在完整的 oop 方面。它被认为是糟糕的设计,主要是因为您无法在其中创建 UML 类和嵌入行为的关系。例如,在具有富域模型 (RDM) 的 Invoice 类中:

    • 类名:Order
    • 已实现:ICommittable、IDraftable、...
    • 属性:No、UserId、TotalAmount、...
    • 行为:Commit()、SaveDraft()、...

    该课程是自我记录的,并自我解释它可以做什么和不能做什么。

    如果是贫血的领域模型,它没有行为,我们需要搜索哪个类负责提交和保存草稿。而且由于 UML 类图只显示了每个类之间的关系(一对多/多对多/聚合/复合),因此无法记录与服务类的关系,Martin Fowler 的观点是对的。

    一般来说,您在服务中发现的行为越多, 您可能会剥夺自己的域名优势 模型。如果你所有的逻辑都在服务中,那你就瞎了眼。

    这是基于Lars Mathiassen 的OOAD 书中的类图UML。不知道较新的类图UML能否表示服务类。

    建议零售价

    在 ADM 的观点和对继承的组合中,RDM(富域模型)违反了 SRP。这可能是真的,但您可以参考此question 进行讨论。

    简而言之,在 ADM 的观点中,SRP 等于一个班级做一件事且只做一件事。 Any change into the class has one and only one reason.

    在 RDM 的观点中,SRP 等同于所有与接口本身相关的责任。一旦操作涉及到其他类,则需要将操作放到其他接口中。实现本身可能会有所不同,例如,如果一个类可以实现 2 个或更多接口。简称为if an operation in interface need to be changed, it is for and only for one reason

    ADM 往往被静态方法滥用,并且可能适用肮脏的黑客攻击

    ADM 很容易被静态方法——服务类滥用。它也可以用 RDM 完成,但它需要另一层抽象,不值得。静态方法通常是糟糕设计的标志,它降低了可测试性,并可能引入竞争条件,以及隐藏依赖关系。

    ADM 可能有许多肮脏的 hack,因为操作不受对象定义的限制(嘿,我可以为此创建另一个类!)。在糟糕的设计师手中,这可能会成为灾难性的。在 RDM 中更难,请阅读下一点以获取信息。

    RDM 的实现通常不能重用,也不能模拟。 RDM 需要事先了解系统的行为

    通常 RDM 的实现不能被重用和模拟。在 TDD 方式中,它降低了可测试性(如果有可以模拟和重用的 RDM,请纠正我)。想象一下这个继承树的情况:

        A
       / \
      B   C
    

    如果 B 需要在 C 中实现逻辑,则无法完成。使用组合而不是继承,可以实现。在 RDM 中,可以这样设计:

        A
        |
        D
       / \
      B   C
    

    其中引入了更多的继承。但是,为了尽早实现整洁的设计,您需要直接了解系统流程。也就是说,RDM 要求您在进行任何设计之前了解系统的行为,否则您将不知道任何适合您系统的名为 ISubmitable、IUpdateable、ICrushable、IRenderable、ISoluble 等的接口。

    结论

    这就是我对这种圣战的全部看法。两者都有优点和缺点。我通常选择 ADM,因为它似乎更高的灵活性甚至更低的可靠性。不管是 ADM 还是 RDM,如果你的系统设计不好,维护就会很困难。任何类型的电锯只有在熟练的木匠手上才会发光。

    【讨论】:

    • 谢谢,这总结了一些有趣的论点。不过,我正在寻找学术或权威参考。除非另有证明,否则我认为它不存在。
    • "RDM 的实现通常不能重用,也不能模拟。"是错误的陈述。事实上,RDM 只能使用 mock 进行正确的单元测试,特别是因为您不希望在单元测试中域对象之间进行交互——这意味着您实际上并不孤立地测试对象。如果您需要注入大量模拟,并编排它们与被测对象的交互,那么您的 SRP 做得不对,而且您的接口不够窄。
    猜你喜欢
    • 1970-01-01
    • 2017-10-29
    • 2010-11-04
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 2010-10-11
    相关资源
    最近更新 更多