老马Repository模式原文
A system with a complex domain model often benefits from a layer, such as the one provided by Data Mapper (165), that isolates domain objects from details of the database access code. In such systems it can be worthwhile to build another layer of abstraction over the mapping layer where query construction code is concentrated. This becomes more important when there are a large number of domain classes or heavy querying. In these cases particularly, adding this layer helps minimize duplicate query logic.
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.
How It Works
Repository is a sophisticated pattern that makes use of a fair number of the other patterns described in this book. In fact, it looks like a small piece of an object-oriented database and in that way it's similar to Query Object (316), which development teams may be more likely to encounter in an object-relational mapping tool than to build themselves. However, if a team has taken the leap and built Query Object (316), it isn't a huge step to add a Repository capability. When used in conjunction with Query Object (316), Repository adds a large measure of usability to the object-relational mapping layer without a lot of effort.
In spite of all the machinery behind the scenes, Repository presents a simple interface. Clients create a criteria object specifying the characteristics of the objects they want returned from a query. For example, to find person objects by name we first create a criteria object, setting each individual criterion like so: criteria.equals(Person.LAST_NAME, "Fowler"), and criteria.like(Person.FIRST_NAME, "M"). Then we invoke repository.matching(criteria) to return a list of domain objects representing people with the last name Fowler and a first name starting with M. Various convenience methods similar to matching (criteria) can be defined on an abstract repository; for example, when only one match is expected soleMatch(criteria) might return the found object rather than a collection. Other common methods include byObjectId(id), which can be trivially implemented using soleMatch.
To code that uses a Repository, it appears as a simple in-memory collection of domain objects. The fact that the domain objects themselves typically aren't stored directly in the Repository is not exposed to the client code. Of course, code that uses Repository should be aware that this apparent collection of objects might very well map to a product table with hundreds of thousands of records. Invoking all() on a catalog system's ProductRepository might not be such a good idea.
Repository replaces specialized finder methods on Data Mapper (165) classes with a specification-based approach to object selection [Evans and Fowler]. Compare this with the direct use of Query Object (316), in which client code may construct a criteria object (a simple example of the specification pattern), add() that directly to the Query Object (316), and execute the query. With a Repository, client code constructs the criteria and then passes them to the Repository, asking it to select those of its objects that match. From the client code's perspective, there's no notion of query "execution"; rather there's the selection of appropriate objects through the "satisfaction" of the query's specification. This may seem an academic distinction, but it illustrates the declarative flavor of object interaction with Repository, which is a large part of its conceptual power.
Under the covers, Repository combines Metadata Mapping (329) with a Query Object (316) to automatically generate SQL code from the criteria. Whether the criteria know how to add themselves to a query, the Query Object (316) knows how to incorporate criteria objects, or the Metadata Mapping (306) itself controls the interaction is an implementation detail.
The object source for the Repository may not be a relational database at all, which is fine as Repository lends itself quite readily to the replacement of the data-mapping component via specialized strategy objects. For this reason it can be especially useful in systems with multiple database schemas or sources for domain objects, as well as during testing when use of exclusively in-memory objects is desirable for speed.
Repository can be a good mechanism for improving readability and clarity in code that uses querying extensively. For example, a browser-based system featuring a lot of query pages needs a clean mechanism to process HttpRequest objects into query results. The handler code for the request can usually convert the HttpRequest into a criteria object without much fuss, if not automatically; submitting the criteria to the appropriate Repository should require only an additional line or two of code.
When to Use It
In a large system with many domain object types and many possible queries, Repository reduces the amount of code needed to deal with all the querying that goes on. Repository promotes the Specification pattern (in the form of the criteria object in the examples here), which encapsulates the query to be performed in a pure object-oriented way. Therefore, all the code for setting up a query object in specific cases can be removed. Clients need never think in SQL and can write code purely in terms of objects.
However, situations with multiple data sources are where we really see Repository coming into its own. Suppose, for example, that we're sometimes interested in using a simple in-memory data store, commonly when we wants to run a suite of unit tests entirely in memory for better performance. With no database access, many lengthy test suites run significantly faster. Creating fixture for unit tests can also be more straightforward if all we have to do is construct some domain objects and throw them in a collection rather than having to save them to the database in setup and delete them at teardown.
It's also conceivable, when the application is running normally, that certain types of domain objects should always be stored in memory. One such example is immutable domain objects (those that can't be changed by the user), which once in memory, should remain there and never be queried for again. As we'll see later in this chapter, a simple extension to the Repository pattern allows different querying strategies to be employed depending on the situation.
Another example where Repository might be useful is when a data feed is used as a source of domain objects梥ay, an XML stream over the Internet, perhaps using SOAP, might be available as a source. An XMLFeedRepositoryStrategy might be implemented that reads from the feed and creates domain objects from the XML.
Further Reading
The specification pattern hasn't made it into a really good reference source yet. The best published description so far is [Evans and Fowler]. A better description is currently in the works in [Evans].
Example: Finding a Person's Dependents (Java)
From the client object's perspective, using a Repository is simple. To retrieve its dependents from the database a person object creates a criteria object representing the search criteria to be matched and sends it to the appropriate Repository.
public class Person {
public List dependents() {
Repository repository = Registry.personRepository();
Criteria criteria = new Criteria();
criteria.equal(Person.BENEFACTOR, this);
return repository.matching(criteria);
}
}
Common queries can be accommodated with specialized subclasses of Repository. In the previous example we might make a PersonRepository subclass of Repository and move the creation of the search criteria into the Repository itself.
public class PersonRepository extends Repository {
public List dependentsOf(aPerson) {
Criteria criteria = new Criteria();
criteria.equal(Person.BENEFACTOR, aPerson);
return matching(criteria);
}
}
The person object then calls the dependents() method directly on its Repository.
public class Person {
public List dependents() {
return Registry.personRepository().dependentsOf(this);
}
}
Example: Swapping Repository Strategies (Java)
Because Repository's interface shields the domain layer from awareness of the data source, we can refactor the implementation of the querying code inside the Repository without changing any calls from clients. Indeed, the domain code needn't care about the source or destination of domain objects. In the case of the in-memory store, we want to change the matching() method to select from a collection of domain objects the ones satisfy the criteria. However, we're not interested in permanently changing the data store used but rather in being able to switch between data stores at will. From this comes the need to change the implementation of the matching() method to delegate to a strategy object that does the querying. The power of this, of course, is that we can have multiple strategies and we can set the strategy as desired. In our case, it's appropriate to have two: RelationalStrategy, which queries the database, and InMemoryStrategy, which queries the in-memory collection of domain objects. Each strategy implements the RepositoryStrategy interface, which exposes the matching() method, so we get the following implementation of the Repository class:
abstract class Repository {
private RepositoryStrategy strategy;
protected List matching(aCriteria) {
return strategy.matching(aCriteria);
}
}
A RelationalStrategy implements matching() by creating a Query Object from the criteria and then querying the database using it. We can set it up with the appropriate fields and values as defined by the criteria, assuming here that the Query Object knows how to populate itself from criteria:
public class RelationalStrategy implements RepositoryStrategy {
protected List matching(Criteria criteria) {
Query query = new Query(myDomainObjectClass())
query.addCriteria(criteria);
return query.execute(unitOfWork());
}
}
An InMemoryStrategy implements matching() by iterating over a collection of domain objects and asking the criteria at each domain object if it's satisfied by it. The criteria can implement the satisfaction code using reflection to interrogate the domain objects for the values of specific fields. The code to do the selection looks like this:
public class InMemoryStrategy implements RepositoryStrategy {
private Set domainObjects;
protected List matching(Criteria criteria) {
List results = new ArrayList();
Iterator it = domainObjects.iterator();
while (it.hasNext()) {
DomainObject each = (DomainObject) it.next();
if (criteria.isSatisfiedBy(each))
results.add(each);
}
return results;
}
}
老马Repository模式原文的更多相关文章
- [转]关于Repository模式
原文链接:关于Repository模式 定义(来自Martin Fowler的<企业应用架构模式>): Mediates between the domain and data mappi ...
- MVC Repository模式
近来发现很多ASP.NET MVC的例子中都使用了Repository模式,比如Oxite,ScottGu最近发布的免费的ASP.NET MVC教程都使用了该模式.就简单看了下. 在<企业架构模 ...
- Asp.Net Core + Dapper + Repository 模式 + TDD 学习笔记
0x00 前言 之前一直使用的是 EF ,做了一个简单的小项目后发现 EF 的表现并不是很好,就比如联表查询,因为现在的 EF Core 也没有啥好用的分析工具,所以也不知道该怎么写 Linq 生成出 ...
- 分享基于Entity Framework的Repository模式设计(附源码)
关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...
- 关于MVC EF架构及Repository模式的一点心得
一直都想写博客,可惜真的太懒了或者对自己的描述水平不太自信,所以...一直都是不想写的状态,关于领域驱动的东西看了不少,但是由于自己水平太差加上工作中实在用不到,所以一直处于搁置状态,最近心血来潮突然 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【二】——使用Repository模式构建数据库访问层
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 在数据访问层应用Repository模式来隔离对领域对象的细节操作是很有意义的.它位于映射层 ...
- (转)MVC中的Repository模式
1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...
- 关于Repository模式
定义(来自Martin Fowler的<企业应用架构模式>): Mediates between the domain and data mapping layers using a co ...
- 探究Repository模式的两种写法与疑惑
现如今DDD越来越流行,园子里漫天都是介绍关于它的文章.说到DDD就不能不提Repository模式了,有的地方也叫它仓储模式. 很多时候我们对Repository都还停留在Copy然后使用的阶段, ...
随机推荐
- MANIFEST.MF Error: No available bundle exports package
Issue: When you imported some 3rd jars and compiled MANIFEST.MF, you may got following compling erro ...
- tomcat服务器虚拟目录的映射方式
lWEB应用程序指供浏览器问的程序,通常也简称为web应用 l l一个web应用由多个静态web资源和动态web资源组成,如: •html.css.js文件 •jsp文件.servlet程序.支持ja ...
- 手动开发动态资源之servlet初步
1.1 静态资源和动态资源的区别 静态资源:当用户多次访问这个资源,资源的源代码永远不会改变的资源. 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变. 1.2动态资源的开发技术 Serv ...
- A*寻路算法入门(一)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- Java进阶(一)Java内存解析
栈.堆.常量池等虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同.本文将深入Java核心,简单讲解Java内存分配方面的知识. 首先我们先来讲解一下内存中的各个区域. stack(栈) ...
- 分布式内存网格Hazelcast源码导读
去年项目需要看了hazelcast源码,当时记录的笔记. Node是节点的抽象,里面包含节点引擎.客户端引擎.分区服务.集群服务.组播服务.连接管理.命令管理.组播属性.节点配置.本地成员.tcp地址 ...
- Java实现堆的封装,进行插入,调整,删除堆顶以完成堆排序实例
简介 堆对于排序算法是一个比较常用的数据结构,下面我就使用Java语言来实现这一算法 首先,我们需要知道堆的数据结构的形式,其实就是一个特殊的二叉树.但是这个二叉树有一定的特点,除了是完全二叉树以外, ...
- ROS(indigo)机器人操作系统学习有趣丰富的Gazebo仿真示例evarobot
一直在寻找一个示例可以将ROS学习中常用的基础内容大部分都包含进去,最好还包括Gazebo仿真, 这样即使没有硬件设备,也可以很好的学习ROS相关内容,但又必须有对应的硬件,便于后续研究. 这里,介绍 ...
- JSON 的数据转换格式(DataTable或DataSet) -善良公社项目
这两天在使用JqueryEasyUI框架绑定数据并实现自动分页时,由于框架的限制需要使用Json数据的来传递与获取数据: JSON的全称是JavaScript Object Notation, 是一种 ...
- javascript之prototype原型属性案例
练习: 给字符串对象添加一个toCharArray的方法,然后再添加一个reverse(翻转)的 方法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...