Repository Pattern由Hieatt and Rob Mee定义为设计模式,使用类似集合的界面来访问域对象和数据映射层,以访问域对象。
基本上,它将一个或多个I / O设备(云,磁盘,数据库等)抽象为一个类似于集合的界面,您可以read, write, seek and delete data。
在Fernando Cejas's Android Clean Architecture上,应用程序所需的所有数据都来自此层,通过存储库实现(接口位于域层中),该实现使用存储库模式,其策略通过工厂选择不同的数据来源取决于某些条件。
然而,正如Douglas Schmidt教授Coursera course指出,内容提供商manages and mediates access to a central repository of data to one or more applications
在书Programming Android中,使用了内容提供商as a Facade for a RESTful Web Service。这种方法最初由 Virgil Dobjanschi during Google I/O 2010提出。
因此,为什么不将内容提供程序用作access the local SQLite database,而不是将其用作存储库模式本身?
答案 0 :(得分:21)
让我们尝试将书籍"Patterns of Enterprise Application Architecture" by Martin Fowler
(with Dave Rice, Matthew Foemmel, Edward Hieatt, Robert Mee, and Randy Stafford)中的知识库模式定义与我们对ContentProviders
的了解进行比较。
这本书指出:
存储库使用的域和数据映射层之间进行协调 用于访问域对象的类似集合的接口。
重要的是accessing domain objects
。所以乍一看似乎存储库模式仅用于访问(查询)数据。但是,使用ContentProvider
,您不仅可以访问(读取)数据,还可以插入,更新或删除数据。
但是,这本书说:
可以尽可能地将对象添加到存储库或从存储库中删除对象 从一个简单的对象集合,以及封装的映射代码 由Repository将在后面进行相应的操作 场景。
所以,是的,Repository和ContentProvider似乎提供相同的操作(非常高级别的观点),尽管该书明确指出simple collection of objects
但ContentProvider
不正确,因为它需要特定于android ContentValues
来自客户(使用某个Cursor
)的1}}和ContentProvider
与之互动。
此外,该书还提到domain objects
和data mapping layers
:
存储库在域和数据映射层之间进行调解
和
在幕后,Repository将元数据映射(329)与查询对象(316)相结合 元数据映射保存元数据中对象关系映射的详细信息。
元数据映射基本上意味着如何将SQL列映射到java类字段。
如前所述,ContentProvider从query()操作返回Cursor
个对象。从我的角度来看,Cursor不是域对象。此外,从游标到域对象的映射必须由客户端(使用ContentProvider)完成。因此,从我的角度来看,ContentProvider中完全缺少数据映射。此外,客户端可能还必须使用ContentResolver
来获取域对象(数据)。在我看来,这个API与书中的定义明显矛盾:
存储库还支持实现清洁分离的目标 和域和数据映射层之间的单向依赖
接下来让我们关注Repository模式的核心思想:
在具有许多域对象类型且很多可能的大型系统中 查询,Repository减少了处理所有代码所需的代码量 继续查询。存储库促进了规范 模式(在这里的例子中以标准对象的形式), 它封装了要在纯面向对象中执行的查询 办法。因此,所有用于设置查询对象的代码都是特定的 案件可以删除。客户端不需要在SQL中思考并且可以编写 纯粹以对象为代码。
ContentProvider需要URI(字符串)。所以它并不是一种真正的面向对象的方式"。此外,ContentProvider可能需要projection
和where-clause
。
因此有人可能认为URI字符串是某种封装,因为客户端可以使用此字符串而不是编写特定的SQL代码,例如:
使用存储库,客户端代码构造条件然后传递 他们到存储库,要求它选择那些对象 比赛。从客户端代码的角度来看,没有查询的概念 "执行&#34 ;;而是选择合适的对象 通过"满意度"查询的规范。
使用URI(字符串)的ContentProvider似乎与该定义不一致,但仍然错过了强调的面向对象的方式。字符串也不是可重用的标准对象,可以通过一般方式重用标准规范,以减少处理所有查询所需的代码量。"
例如,要按名称查找人物对象,我们首先要创建一个标准 对象,设置每个单独的标准,如下所示: criteria.equals(Person.LAST_NAME," Fowler"),和 criteria.like(Person.FIRST_NAME," M")。然后我们调用 repository.matching(criteria)返回域对象列表 代表姓Fowler和名字的人 从M开始。
正如您已经说过的(在您的问题中),存储库对于隐藏不同的数据源也很有用,因为它们是客户端不了解的实现细节。 这适用于ContentProviders并在书中指定:
存储库的对象源可能不是关系数据库 完全没问题,因为Repository非常容易使用它 通过专门的战略取代数据映射组件 对象。因此,它在具有的系统中特别有用 域对象的多个数据库模式或源,以及 在测试期间,需要使用专门的内存中对象 速度。
和
因为Repository的界面可以保护域层免受意识 对于数据源,我们可以重构查询的实现 存储库中的代码,而不更改来自客户端的任何调用。 实际上,域代码不需要关心源或目标 域对象。
总结:Martin Fowler等人的一些定义。 book匹配ContentProvider的API(如果你忽略了本书强调面向对象的事实):
但是,ContentProvider确实错过了本书中描述的存储库模式的一些关键点:
criteria.equals(Person.LAST_NAME, "Fowler")
这样的Criteria对象可以重复使用并用于组合匹配的标准(因为你必须使用字符串)。Cursor
时完全错过数据映射。这非常糟糕,因为客户端(使用ContentProvider访问数据)必须将Cursor映射到域对象。此外,这意味着客户端具有存储库内部的知识,例如列的名称。 "存储库可以是一种很好的机制,可以提高广泛使用查询的代码的可读性和清晰度。"对于ContentProviders来说,情况确实如此。所以不,内容提供者不是“知识库模式”和“#34;企业应用程序架构模式”中定义的“存储库模式”的实现。因为它错过了我上面提到的至少两件必要的事情。
另外,请注意,正如本书的名称所示,存储库模式旨在用于企业应用程序,您可以在其中执行大量查询。
Android开发人员倾向于使用术语"存储库模式"但实际上并不意味着"原创" Fowler等人描述的模式。 (Criterias对查询的高可重用性),而是指隐藏底层数据源(SQL,云,无论如何)和域对象映射的接口。
更多信息:http://hannesdorfmann.com/android/evolution-of-the-repository-pattern
答案 1 :(得分:6)
简短回答:Contentprovider是数据源,而不是存储库。
SQL-Database / Android-Contentproviders / Repositories的目的是创建/读取/更新/删除/查找数据
存储库通常在高级业务上运行特定的java类(如Customer,Order,Product,....) 而SQL-Database和Android-Contentproviders作为数据源在低级表,行和列上运行。
因为SQL数据库不是存储库所以 Android-Contentprovider也不是存储库。
但您可以使用基础Contentprovider
来实现存储库答案 2 :(得分:5)
我会提到Dianne Hackborn(来自Android Framework团队)给出我的意见。
的ContentProvider
最后,ContentProvider是一个相当专业的工具,用于将数据从应用程序发布到其他地方。人们通常认为它们是数据库的抽象,因为对于这种常见情况,它们内置了很多API和支持......但从系统设计的角度来看,这不是他们的观点。
这些对系统的影响是应用程序的入口点,用于发布由URI方案标识的命名数据项。因此,应用程序可以决定如何将其包含的数据映射到URI命名空间,将这些URI分发给其他实体,这些实体又可以使用它们来访问数据。有一些特殊的东西可以让系统管理app:
•分发URI并不要求应用程序继续运行,因此这些应用程序可能会在拥有应用程序已经死亡的情况下到处运行。只有在有人告诉系统的时候,他才会给我这个URI的数据"是否需要确保拥有该数据的应用程序正在运行,因此它可以要求应用程序检索并返回数据。
•这些URI还提供了重要的细粒度安全模型。例如,应用程序可以将其拥有的图像的URI放在剪贴板上,但保留其内容提供程序,以便任何人都无法自由访问它。当另一个应用程序从剪贴板中提取该URI时,系统可以为其提供临时的" URI权限授予"因此,只允许在该URI后面访问数据,但在应用程序中没有其他内容。
我们不关心的事情:
如何实现内容提供商背后的数据管理并不重要;如果您不需要在SQLite数据库中使用结构化数据,请不要使用SQLite。例如,FileProvider帮助程序类是一种通过内容提供程序在您的应用程序中提供原始文件的简便方法。
此外,如果您不是从您的应用发布数据供其他人使用,则根本不需要使用内容提供商。确实如此,由于围绕内容提供者构建了各种帮助程序,这可以是将数据放入SQLite数据库并使用它来填充像ListView这样的UI元素的简单方法。但是,如果这些东西中的任何一个使得您尝试做的事情变得更加困难,那么请随意使用它,而是为您的应用程序使用更合适的数据模型。
全文: https://plus.google.com/+DianneHackborn/posts/FXCCYxepsDU
答案 3 :(得分:4)
对于这个问题的赞誉,这是一个很好的观察:)。恕我直言,这不是一个肯定或没有问题,因为它是相当普遍的,因为大多数设计模式相关的主题是。答案取决于您考虑的具体情况:
如果您的应用完全依赖于平台,这意味着要考虑Android生态系统的仅上下文,那么是,ContentProvider是Repository模式的实现即可。这里的论点是,内容提供商设计来解决存储库模式旨在解决的一些相同挑战:
如果您将所有上述内容与存储库模式的原则并排放置,则存在一些严重的相似之处。并非所有人都满意,但核心思想是一样的。
现在,考虑到在多种环境(即网络,移动设备,PC)中大规模应用的应用,需求会完全改变。 这是一个坏主意,因为每个人都建议依赖ContentProvider作为设计模式。 它本身并不一定是个坏主意,但必须实现设计模式,以便其他人能够尽快理解您的代码。你看,即使在这里,每个人都建议使用ContentProvider:作为数据源,或者无论如何依赖于平台。因此,如果您在具有已知目的的组件之上强制实施,事情就会变得相当不清楚。以经典模式组织代码要好得多。
tl; dr; 如果您的应用在您的Android设备上被隔离,您肯定可以合并这两个概念。如果您的应用程序用于更大规模,在多个平台上使用更清晰,以经典方式组织您的代码。
答案 4 :(得分:2)
这是一个有趣的问题。我认为我的第一个答案是否定的,Content Provider不是Repository Pattern的实现。
正如您所提到的,存储库模式旨在将业务逻辑(域)与数据层分开。这种方法允许您为业务逻辑创建单元测试(因此域根本不应该依赖于Android)。通过使用内容提供商,您需要在您的域中拥有某种Android对象。
您可以想象一种隐藏接口背后的内容提供程序逻辑的方法,但是您将放弃内容提供程序允许您执行的许多好东西。
如果您对Android架构感兴趣,我建议您查看一下这个Github项目Android Clean Architecture。您将找到一种分离演示文稿,域和数据层的好方法,并且使用存储库模式完成域和数据之间的通信。
希望这会有所帮助!
答案 5 :(得分:1)
恕我直言,最好将Contentprovider视为数据源,尽管数据可以通过多种方式存储(SQLite数据库,文件......),以保持架构和Android框架之间的某种独立性
Google存储库提供了一些架构示例。其中一个包含带有内容提供者和存储库的体系结构示例:
googlesamples/android-architecture/todo-mvp-contentproviders
精选摘录:
然后,您可以使用内容提供程序来支持此示例未涵盖的其他功能,从而提供以下可能的好处:
- 允许您与其他应用安全地共享应用中存储的数据。
- 在您的应用中添加对自定义搜索的支持。
- 开发访问应用中数据的小部件。
答案 6 :(得分:0)
Content Provider是一个Android
组件,如果将存储库概念与此组件混合,气味将不会很好,它会对您的应用程序产生阻塞依赖。
答案 7 :(得分:0)
将ContentProviders用作存储库的问题在于您将模型中的依赖项添加到Android Framework。使用存储库模式可以轻松地模拟,测试和替换实现。
正确的方法是在接口下隐藏ContentProvider,让模型通过此接口访问数据。这样,您的代码就可以与平台分离。
基本上,ContentProvider是您要抽象的I / O源。