假设您的应用程序管理Person 对象,每个实例具有name、age 和jobTitle 属性。
您想持久化这些对象,从持久化介质中检索它们,并可能更新(例如,在它们的生日,增加年龄)或删除。这些任务通常被称为 CRUD,来自 Create、Read、Update 和 Delete。
最好将您的“业务”逻辑与处理Person 对象持久性的逻辑分离。这允许您在不影响业务逻辑的情况下更改持久性逻辑(例如,从 DB 到分布式文件系统)。
您可以通过将所有持久性逻辑封装在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"。
另一个持久层可以是文件系统,或者选择将Person 对象存储在两个表中的另一种数据库格式,PersonAge 和PersonJobTitle。
在后一种情况下,DataMapper 的任务是将johnDoe 对象转换为两行:一行用于PersonAge 表,另一行用于PersonJobTitle 表。然后查询逻辑需要将功能查询转换为两个表上的join。最后,DataMapper 需要知道如何根据查询结果构造一个Person 对象。
在大型、复杂的系统中,您希望使用小型组件来完成明确定义的小型工作,并且可以独立开发和测试:
- 业务逻辑在想要读取或持久化对象时处理
Repository,而不关心它是如何实现的。
-
Repository 处理DataMapper 在特定持久性介质中读取/写入对象时。
- 对于查询,
Repository 依赖于由DataMapper 提供的模式(例如,jobTitle 值在PersonTable 表的JobTitle 列中找到)但不依赖于任何实现 的映射器。
- 对于 DB 持久性,
DataMapper 依赖于一个 DB 层,它可以保护它免受 Oracle/Sybase/MSSQL/OtherProvider 详细信息的影响。
这些模式没有“不同”,它们只是暴露了不同的基本特征。