数据映射器、表数据网关(网关)、数据访问对象(DAO)和仓储库模式之间有什么区别?

146

我想要提高自己的设计模式技能,我很好奇这些模式之间有什么区别?它们似乎都是相同的东西 - 封装特定实体的数据库逻辑以使调用代码不了解底层持久性层。根据我的简要研究,它们通常都实现标准的CRUD方法并抽象出数据库具体细节。

除命名约定 (例如:CustomerMapper vs. CustomerDAO vs. CustomerGateway vs. CustomerRepository) 外,还有什么区别吗?如果有区别,你会在什么情况下选择其中之一?

过去,我会编写类似以下简化代码(通常不使用公共属性):

public class Customer
{
    public long ID;
    public string FirstName;
    public string LastName;
    public string CompanyName;
}

public interface ICustomerGateway
{
    IList<Customer> GetAll();
    Customer GetCustomerByID(long id);
    bool AddNewCustomer(Customer customer);
    bool UpdateCustomer(Customer customer);
    bool DeleteCustomer(long id);
}

我有一个CustomerGateway类,它实现了所有方法的特定数据库逻辑。有时我不使用接口,并将CustomerGateway上的所有方法都设置为静态的(我知道这样做会使其更难测试),这样我就可以像这样调用:

Customer cust = CustomerGateway.GetCustomerByID(42);

这似乎是数据映射器和存储库模式的相同原则; DAO模式(我认为与网关是同一件事?)也似乎鼓励使用特定于数据库的网关。

我有什么遗漏吗?拥有3-4种完全相同的方法似乎有点奇怪。

5个回答

104

你提到的这些术语:DataMapper、DAO、DataTableGateway和Repository,都有着类似的目的(当我使用其中一个时,我期望能够得到一个Customer对象),但意图/含义和实现方式不同。

Repository被认为是一种"具有更加精细的查询功能的集合"[Evans, Domain Driven Design],可以被视为"内存中的对象门面"(Repository discussion)

DataMapper "在保持对象及其自身与映射器的独立性的同时,在对象和数据库之间移动数据" (Fowler, PoEAA, Mapper)

TableDataGateway"访问数据库表的网关(封装对外部系统或资源的访问的对象)。一个实例处理表中的所有行" (Fowler, PoEAA, TableDataGateway)

DAO是指将数据资源的客户端接口与其数据访问机制分离/将特定数据资源的访问API适配到通用客户端接口中,从而使得“使用数据的代码”能够独立于“数据访问机制”进行变化(Sun Blueprints)。

Repository似乎非常通用,没有暴露任何数据库交互的概念。 DAO提供了一个接口,使得可以使用不同的底层数据库实现。 TableDataGateway是一个围绕单个表的薄包装器。 DataMapper作为中介,使得模型对象能够独立于数据库表示形式(随时间)而演变。


17
实际上,DAO和TableDataGateway之间没有很大的区别,在[Fowler, PoEAA]中他们确切地说道:“[Alur et al.]讨论了数据访问对象模式,它是一个Table Data Gateway……我使用了不同的名称,部分原因是我认为这个模式是更一般的网关(466)概念的特定用法,我希望模式名称能够反映出这一点。” - Miguel Gamboa
10
好的。我的印象是,PoEAA对于TableDataGateway的定义比DataAccessObject更为狭窄。前者似乎意味着与一个(关系型)数据库表之间存在一对一的映射,而DAO可以作为多个底层非关系型资源的Facade。DAO的重点在于能够替换基础数据存储,而TableDataGateway的重点在于封装针对单个表的SQL操作(不一定以数据存储中立/可移植的方式实现)。 - Pierce Hickey

36

在软件设计领域(至少我是这样感觉的)存在一种趋势,即为已知的旧事物和模式发明新名称。当我们有一个新的范例(可能与已经存在的事物略有不同)时,它通常会带来每个阶层的全套新名称。所以,“业务逻辑”就因为我们说我们做SOA而变成了“服务层”,DAO就因为我们说我们做DDD而变成了Repository(但实际上每个概念并不是多么新和独特,只是针对相同主题的已知概念以新名称聚合在同一本书中)。所以我并不是说所有这些现代范例和缩写完全意味着相同的事情,但你真的不应该太过担心。大多数情况下,这些都是相同的模式,只是来自不同的家庭。


4
@MladenMihajlovic,仅因您不理解或不同意,并不意味着这个答案无效或不正确。 - Cypher
2
@MladenMihajlovic,这不是这个答案所说的。最后一句话总结了一切。 - Cypher
2
@Cypher 这些模式大多相同吗?不,它们并不相同。网关模式的实现与存储库模式的实现不同。对于未经训练的人来说,它们可能看起来相同,但实际上并非如此。此外,正如Mladen Mihajlovic正确指出的那样,这个答案是相当错误的。业务逻辑和服务层是两个不同的东西。 - Frederik Krautwald
1
@Cypher 这并不是一个观点问题,而是事实。网关模式是由Martin Fowler在他的PoEAA中制定的,主要与Facade或Adapter模式[GoF]相关。区别在于网关是为特定用途编写的,通常不存在现有接口。网关通常只涉及两个对象,并且被包装的资源对网关没有任何了解。(继续...) - Frederik Krautwald
4
这更像是一条评论而不是答案。 - Pétur Ingi Egilsson
显示剩余2条评论

35

数据映射器(Data Mapper)和表数据网关(Table Data Gateway) 简而言之:

  • 数据映射器将接收领域模型对象(Entity)作为参数,并使用它来实现CRUD操作。
  • 表数据网关将接收方法的所有参数(作为原始类型),并且不会知道领域模型对象(Entity)的任何信息。

    最终,它们都将充当内存中对象与数据库之间的中介。


  • 2
    更新链接:https://github.com/willdurand-edu/php-slides/blob/master/src/common/09_databases.md - Fernando Correia

    15

    您说得很好。选择您最熟悉的那一个。我想指出一些可能有助于澄清的事情。

    Table Data Gateway 主要用于单个表或视图。它包含了所有的 select、insert、update 和 delete 操作。所以在您的情况下,Customer 是一个表或一个视图。因此,一个 Table Data Gateway 对象实例处理表中的所有行。通常,这与每个数据库表相关联的是一个对象。

    Data Mapper 则更加独立于任何领域逻辑,耦合度更低(尽管我认为无论是耦合还是不耦合都存在)。它只是一个传输数据的中间层,在保持对象和数据库之间及映射器本身相互独立的同时传输数据。

    因此,通常在 Mapper 中,您会看到像 insert、update、delete 这样的方法;而在 Table Data Gateway 中,您会发现 getcustomerbyId、getcustomerbyName 等方法。

    数据传输对象与以上两种模式不同,主要是因为它是一种分布式模式,而不是以上两种模式的数据源模式。当您使用远程接口并且需要使调用更少昂贵时,主要使用它。因此,通常设计一个可在网络上传输的 DTO,可以将所有数据带回服务器进行进一步的业务规则或处理。

    我不太熟悉仓储库模式,因为我还没有机会使用它,但我会看其他人的答案。


    作者询问的是 DAO(数据访问对象),而不是 DTO(数据传输对象)。 - MingalevME

    4
    以下是我的理解: TableGateWay/RowDataGateWay: 在这个上下文中,Gateway指的是一个特定的实现,每个“域对象”都映射到每个“域对象网关”。例如,如果我们有Person,那么我们将有一个PersonGateway来将域对象Person存储到数据库中。如果我们有Person、Employee、Customer等,我们将有PersonGateway、EmployeeGateway和CustomerGateway。每个网关都会为该对象具有特定的CRUD函数,它与其他网关无关。这里没有可重用的代码/模块。网关可以进一步分为RowDataGateway或TableGateway,取决于您传递的是“id”还是“object”。通常将网关与Active Record进行比较。它将您的域模型与数据库架构联系起来。 Repository/DataMapper/DAO:它们是相同的东西。它们都指代将数据库实体转换为域模型的持久化层。与网关不同,Repository/DataMapper/DAO隐藏了实现细节。您不知道Person背后是否有PersonGateway。它可能有,也可能没有,您不需要关心。所有你需要知道的是它必须支持每个域对象的CRUD操作。它将数据源和域模型解耦。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接