【问题标题】:DTO objects for each entity每个实体的 DTO 对象
【发布时间】:2014-02-05 21:00:00
【问题描述】:

我继承了一个用 Java 编写的应用程序,它使用 JPA 访问数据库。该应用程序使用了一种我以前没有遇到过的设计模式,我真的会就为什么使用这种模式提供一些指导。像许多应用程序一样,我们有前端、中间件和后端数据库。该数据库是通过 DAO 访问的。 DAO 上的每个方法都加载一个实体 DTO,它只是一个 POJO,只有 getter 和 setter,然后将该实体 DTO 传递到具有其他更改实体状态的方法的实体属性中。一个例子【改名保护无辜者】

enum Gender
{
    Male,
    Female
}

class PersonDTO
{
    private String mFirstName;
    private String mLastName;
    private Gender mGender;
    ...

    String getFirstName() { return this.mFirstName; }
    String setFirstName(String name) { this.mFirstName = name; }
    // etc
}

class Person
{
    PersonDTO mDTO;
    Person(PersonDTO dto)
    {
        mDTO = dto;
    }

    String getFirstName() { return mDTO.getFirstName() }
    String setFirstName(String name) { mDTO.setFirstName(name); }
    // and so on

    void marry( Person aNotherPerson )
    {
        if( this.getGender()==Gender.Female && 
               aNotherPerson.getGender()==Gender.Male)
        {
            this.setLastName( aNotherPerson.getLastName() );
        }
        aNotherPerson.marry( this );
    }
}

这在 30 个左右的实体类中重复出现,使用 DTO 加倍到 60 个,我就是不明白为什么。我了解 (bits) 关于 converns 的分离,并且我也了解 (bits) 基于 EAO 的设计与基于活动记录的设计之间的区别。

但它真的必须走这么远吗?是否总是至少有一个“DB”对象只包含映射到 DB 字段的 getter 和 setter?

【问题讨论】:

    标签: java entity-framework design-patterns jpa dto


    【解决方案1】:

    免责声明:关于这个主题有不同的意见,并且根据您的系统架构,您可能别无选择。

    话虽如此...我以前见过这种模式的实现,但不是它的忠实拥护者,在我看来是重复大量代码而没有增加任何实际价值。它似乎在具有 XML API(如 SOAP)的系统中特别流行,在这种系统中,可能难以将 XML 结构直接映射到您的对象结构。在您的特定情况下,它似乎更糟,因为在重复的 getFirstName()/getLastName() 方法之上,有业务逻辑(属于服务层)直接编码到 pojo(应该是一个简单的数据传输对象像 DTO)。为什么pojo要知道只有异性才能结婚?

    为了帮助更好地理解为什么,您能否解释一下这些 DTO 的来源?是否有前端将数据提交给控制器,然后控制器将其转换为 DTO,然后用于使用数据填充实体属性?

    【讨论】:

    • DTO 是 Web 服务调用的结果,这些调用都是由计时器运行的一个类处理的。它关闭,调用 Web 服务,填写 DTO,持久化它们,然后创建保存在内存中的“实体”。然后前端访问实体并通过控制器对它们进行变异。至于“为什么pojo要知道只有异性才能结婚?” (21 世纪的坏例子!)-这不是 OOP 的核心部分吗?所以你可以说有一个 CelebrityPerson 覆盖了结婚()并且不取他们的配偶姓名?与你的服务层中的类型的开关取决于类型?
    • @JamesHobson - 问题是 Person 为什么要处理婚姻?还有离婚的方法吗?给人们发电子邮件让他们知道离婚已经敲定(现在你的 Person 类知道电子邮件)怎么样?随着您添加新功能,方法列表可以无限长。这就是为什么应该有婚姻服务来处理结婚和离婚操作,这样 POJO 就不必知道它被使用的每个场景。关注点分离将是这里的指导 OOP 原则。至于处理不同的婚姻类型,请查看策略模式。
    • @JamesHobson - 我想这归结为 Person 类看起来很可疑,很像典型 N 层应用程序中的 Service。考虑编写单元测试。如果 Person 是一个没有 getFirstName()/getLastName() 的服务,你只需测试结婚(Person person1,Person person2)。但在此设置中,您还必须测试您的 setter 和 getter。
    • 结婚可能是一个不好的例子,因为你并没有真正“告诉”一个人结婚,而且它可能涉及许多其他操作,而不仅仅是两个人。我想我要讨论的是旧的AnemicDomainModel 论点-为什么域对象不能有一些方法可以改变自己的状态,而不仅仅是基本的get/set,可能涉及其他对象?关于这一切的内容很多,我想就像任何事情一样,它只是意见——而且我读得越多,我就越困惑;)
    【解决方案2】:

    也可能是他们使用它只是为了将 JPA 注释与富域对象分开。

    所以我猜有人不喜欢在一个类中包含 JPA 注释和丰富的域对象行为。也有人可能会争辩说,JPA 注释和富域对象不应该在同一层(因为注释混合了关注点),所以如果你赢得了这个论点,你就会得到这种分离。

    另一个你会看到这种事情发生的地方是当你想从丰富的域对象中抽象出类似的注释(例如 web 服务中的 jaxb 注释)。

    所以意图可能是 DTO 充当从代码到数据库的序列化机制,这与 here by martin fowler 提到的意图非常相似。

    【讨论】:

    • 谢谢 - 这就是我在“不喜欢在一个类中拥有 JPA 注释和丰富的域对象行为”中寻找的原因
    【解决方案3】:

    这似乎不是已知的模式。

    一般

    • 通常维护一个单独的对象来表示数据库中的记录,称为域对象。
    • 对象上的 CRUD 操作是 DAO 类的一部分,其他业务操作将是 Manager 类的一部分,但这些类都没有将域对象存储为成员变量,即 DAO 和 Manager 都没有携带状态。它们只是处理处理作为参数传入的域对象的元素。
    • DTO 用于前端和后端之间的通信,以呈现来自 DB 的数据或接受来自最终用户的输入
    • DTO 由 Manager 类转换为域对象,其中根据业务规则执行验证和修改。此类域对象使用 DAO 类保存在数据库中。

    【讨论】:

    • 缺少“经理”类是我认为令我困惑的地方。您是否会说在实体中拥有类似 PersonManager.marrayPeople(Person person1, Person person2) 等而不是这些操作会更令人兴奋?
    • 没错@JamesHobson,结婚方法应该是经理类的一部分。
    【解决方案4】:

    我参与过一个项目,我们有 DTO,其唯一目的是将信息从前端控制器传输到某个外观层。然后外观层负责将这些 DTO 转换为域对象。

    这种分层背后的想法是将前端(视图)与域分离。有时 DTO 可以包含多个用于聚合视图的域对象。但是领域层总是呈现干净、可重用、可缓存(如果需要)的对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-16
      • 1970-01-01
      • 1970-01-01
      • 2017-04-20
      • 2013-09-14
      • 2016-12-06
      • 1970-01-01
      相关资源
      最近更新 更多