假设您的应用程序管理具有名称、年龄和职务属性的“Person”对象实例。您希望将这些对象持久化、从持久化介质中检索它们,以及可能更新(例如在生日时增加年龄)或删除它们。这些任务通常称为 CRUD,即创建 Create、读取 Read、更新 Update 和删除 Delete。
最好将业务逻辑与处理“Person”对象持久性的逻辑分离。这样可以更改持久性逻辑(例如从数据库转移到分布式文件系统)而不影响业务逻辑。您可以通过将所有持久性逻辑封装在一个“Repository”之后来实现此目的。一个假想的“PersonRepository”(或“Repository<Person>”)将使您能够编写如下代码:
Person johnDoe = personRepository.get(p=> p.name == "John Doe")
johnDoe.jobTitle = "IT Specialist"
personRepository.update(johnDoe)
这只是业务逻辑,并不关心对象存储的方式和位置。
Repository
的另一侧,您同时使用了 DataMapper
和将功能描述 (p=> p.name == "John Doe"
) 转换为持久层可以理解的内容的查询翻译器。
您的持久层可以是数据库,在这种情况下,DataMapper
将会把 Person
对象转换成一个 PersonsTable
表中的一行数据,而查询翻译器则会将功能查询转换成 SELECT * FROM PersonsTable WHERE name == "John Doe"
。
另一种持久层可以是文件系统,或者是选择在两个表 (PersonAge
和 PersonJobTitle
) 中存储 Person
对象的另一种数据库格式。
在后一种情况下,DataMapper
的任务是将 johnDoe
对象转换为 2 行数据:一个用于 PersonAge
表,一个用于 PersonJobTitle
表。然后查询逻辑需要将功能查询转换为两个表之间的 join
。最后,DataMapper
需要知道如何从查询结果构建一个 Person
对象。
在大型复杂系统中,您希望使用小的组件来处理小的、明确定义的任务,这些组件可以独立开发和测试:
- 业务逻辑需要读取或持久化对象时,会处理
Repository
,而不关心其如何实现。
Repository
处理 DataMapper
,以便在特定的持久性介质中读写对象。
- 对于查询,
Repository
依赖于由 DataMapper
提供的模式 (例如,jobTitle
的值可以在 PersonTable
表中的 JobTitle
列中找到),但不依赖于任何映射器的实现。
- 对于数据库持久性,
DataMapper
依赖于一个 DB 层,该层屏蔽了它与 Oracle/Sybase/MSSQL/OtherProvider 的细节。
这些模式并没有“不同”,它们只是展示了不同的基本特征。