Content Provider是Repository Pattern的实现吗?

JP *_*ura 36 android design-patterns android-mvp

存储库模式Hieatt和Rob Mee定义为设计模式,模式使用类似集合的接口来访问域对象,从而在域和数据映射层之间进行调解.

MSDN存储库模式

基本上,它将一个或多个I/O设备(云,磁盘,数据库等)抽象为一个类似于集合的接口,您可以在其中读取,写入,查找和删除数据.

Fernando Cejas的Android清洁架构中,应用程序所需的所有数据都来自此层,通过存储库实现(接口位于域层中),该实现使用存储库模式,其策略通过工厂选择不同的数据源,具体取决于一定条件下.

内容提供商

然而,正如Douglas Schmidt教授在Coursera课程中所指出的,内容提供商管理和调解对一个或多个应用程序的中央数据库的访问.

内容提供商

Android编程一书中,内容提供程序用作RESTful Web服务的Facade.这种方法最初由 Virgil Dobjanschi在Google I/O 2010期间提出.

因此,为什么不使用它作为存储库模式本身而不是使用内容提供程序来访问本地SQLite数据库

在此输入图像描述

soc*_*qwe 26

让我们尝试比较Martin Fowler(与Dave Rice,Matthew Foemmel,Edward Hieatt,Robert Mee和Randy Stafford)的"企业应用程序架构的模式 "一书中的Repository Pattern定义与我们所知道的ContentProviders.

该书指出:

存储库使用类似集合的接口访问域对象,在域和数据映射层之间进行调解.

重要的是accessing domain objects.所以乍一看似乎存储库模式仅用于访问(查询)数据.ContentProvider但是,使用a ,您不仅可以访问(读取)数据,还可以插入,更新或删除数据.但是,这本书说:

可以从简单的对象集合中向对象添加对象或从对象库中删除对象,并且由存储库封装的映射代码将在后台执行适当的操作.

所以,是的,Repository和ContentProvider似乎提供了相同的操作(非常高级别的观点),尽管该书明确指出simple collection of objects哪些不适用,ContentProvider因为它需要特定于android ContentValues并且Cursor来自Client(使用某个ContentProvider)与之交互.

此外,书中提到domain objectsdata mapping layers:

存储库在域和数据映射层之间进行调解

在幕后,Repository将元数据映射(329)与查询对象(316)相结合.元数据映射保存元数据中对象关系映射的细节.

元数据映射基本上意味着如何将SQL列映射到java类字段.

如前所述,ContentProvider Cursor从query()操作返回一个对象.从我的角度来看,Cursor不是域对象.此外,从游标到域对象的映射必须由客户端(使用ContentProvider)完成.因此,从我的角度来看,ContentProvider中完全缺少数据映射.此外,客户端可能还必须使用a ContentResolver来获取域对象(数据).在我看来,这个API与书中的定义明显矛盾:

存储库还支持在域和数据映射层之间实现清晰分离和单向依赖的目标

接下来让我们关注Repository模式的核心思想:

在具有许多域对象类型和许多可能查询的大型系统中,Repository减少了处理所有查询所需的代码量.存储库提升了规范模式(在此处示例中以条件对象的形式),它封装了以纯面向对象的方式执行的查询.因此,可以删除在特定情况下设置查询对象的所有代码.客户端不需要在SQL中思考,并且可以纯粹根据对象编写代码.

ContentProvider需要URI(字符串).所以它并不是一种"面向对象的方式".ContentProvider也可能需要a projection和a where-clause.

因此有人可能会争辩说URI字符串是某种封装,因为客户端可以使用此字符串而不是编写特定的SQL代码,例如:

使用存储库,客户端代码构造条件,然后将它们传递给存储库,要求它选择匹配的对象.从客户端代码的角度来看,没有查询"执行"的概念; 而是通过查询规范的"满意度"选择适当的对象.

使用URI(字符串)的ContentProvider似乎与该定义不矛盾,但仍然错过了强调的面向对象的方式.字符串也不是可重用的标准对象,可以通过一般方式重用标准规范,以"减少处理所有查询所需的代码量".

例如,要按名称查找人员对象,我们首先创建一个条件对象,设置每个单独的标准,如:criteria.equals(Person.LAST_NAME,"Fowler")和criteria.like(Person.FIRST_NAME,"M").然后我们调用repository.matching(criteria)来返回一个域对象列表,这些域对象代表姓氏为Fowler且名字以M开头的人.

正如您已经说过的(在您的问题中),存储库也可用于隐藏不同的数据源,作为客户端不知道的实现细节.这适用于ContentProviders并在本书中指定:

Repository的对象源可能根本不是关系数据库,这很好,因为Repository很容易通过专门的策略对象替换数据映射组件.由于这个原因,它在具有多个数据库模式或域对象源的系统中以及在测试期间特别适用于速度时需要专门使用内存中对象时.

由于Repository的接口使域层不受数据源的识别,因此我们可以在Repository中重构查询代码的实现,而无需更改来自客户端的任何调用.实际上,域代码不需要关心域对象的源或目标.


总而言之:Martin Fowler等人的一些定义.book匹配ContentProvider的API(如果你忽略了本书强调面向对象的事实):

  • 隐藏存储库/ ContentProvider具有不同数据源的事实
  • 客户端永远不必在像SQL这样的数据源特定DSL中编写查询.如果我们认为URI不是特定于数据源的,那么ContentProvider就是如此.
  • Repository和ContentProvider都具有相同的"高级"操作集:读取,插入,更新和删除数据(如果忽略了Fowler谈论面向对象和对象集合的事实,而ContentProvider使用Cursor和ContentValues)

但是,ContentProvider确实错过了本书中描述的存储库模式的一些关键点:

  • 由于ContentProvider使用URI(也是where子句的字符串),因此客户端无法重用匹配条件对象.这是一个值得注意的重要事项.本书明确指出存储库模式很有用"在具有许多域对象类型和许多可能的查询的大型系统中,存储库减少了处理所有查询所需的代码量".不幸的是,ContentProvider没有像criteria.equals(Person.LAST_NAME, "Fowler")这样的Criteria对象可以重用并用于组合匹配的标准(因为你必须使用字符串).
  • ContentProvider在返回a时完全错过数据映射Cursor.这非常糟糕,因为客户端(使用ContentProvider访问数据)必须将Cursor映射到域对象.此外,这意味着客户端具有存储库内部的知识,例如列的名称."存储库可以成为一种很好的机制,可以提高广泛使用查询的代码的可读性和清晰度." 对于ContentProviders来说,情况确实如此.

所以不,ContentProvider不是 "企业应用程序架构模式"一书中定义的Repository模式的实现,因为它错过了我上面指出的至少两个基本内容.

另请注意,正如本书的名称所示,存储库模式旨在用于执行大量查询的企业应用程序.

Android开发人员倾向于使用术语"存储库模式",但实际上并不意味着Fowler等人描述的"原始"模式.(Criterias对查询的高可重用性),而是指隐藏底层数据源(SQL,云,无论如何)和域对象映射的接口.

更多信息:http://hannesdorfmann.com/android/evolution-of-the-repository-pattern


k3b*_*k3b 7

简短回答:Contentprovider是数据源而不是存储库.

SQL-Database/Android-Contentproviders/Repositories的目的是创建/读取/更新/删除/查找数据

存储库通常在高级业务特定的Java类(如Customer,Order,Product,...)上运行,而SQL-Database和Android-Contentproviders在低级表,行和列上作为数据源运行.

因为SQL-Database不是存储库所以Android-Contentprovider也不是存储库.

但您可以使用基础Contentprovider实现存储库


ngl*_*ber 6

我会提到Dianne Hackborn(来自Android Framework团队)给出我的看法.

内容提供商

最后,ContentProvider是一个相当专业的工具,用于将数据从应用程序发布到其他地方.人们通常认为它们是数据库的抽象,因为对于这种常见情况,它们内置了很多API和支持......但从系统设计的角度来看,这不是他们的观点.

它们对系统的作用是用于发布由URI方案标识的命名数据项的应用程序的入口点.因此,应用程序可以决定如何将其包含的数据映射到URI命名空间,将这些URI分发给其他实体,这些实体又可以使用它们来访问数据.有一些特殊的东西,它允许系统管理应用程序:

•分发URI并不需要应用程序继续运行,因此这些应用程序可以在拥有应用程序已经死亡的情况下到处运行.只有在有人告诉系统的时候,"嘿给我这个URI的数据"是否需要确保应用程序拥有该数据正在运行,因此它可以要求应用程序检索并返回数据.

•这些URI还提供了重要的细粒度安全模型.例如,应用程序可以将其拥有的图像的URI放在剪贴板上,但保留其内容提供程序,以便任何人都无法自由访问它.当另一个应用程序从剪贴板中提取该URI时,系统可以为其提供临时"URI权限授予",以便允许它仅访问该URI后面的数据,但应用程序中没有任何其他内容.

我们不关心的是:

如何实现内容提供商背后的数据管理并不重要; 如果您不需要SQLite数据库中的结构化数据,请不要使用SQLite.例如,FileProvider助手类是一种通过内容提供商提供应用程序中的原始文件的简便方法.

此外,如果您不是从您的应用发布数据供其他人使用,则根本不需要使用内容提供商.确实如此,由于围绕内容提供者构建了各种帮助程序,这可以是将数据放入SQLite数据库并使用它来填充像ListView这样的UI元素的简单方法.但是,如果这些东西中的任何一个使得您尝试做的事情变得更加困难,那么请随意使用它,而是为您的应用程序使用更合适的数据模型.

全文:https: //plus.google.com/+DianneHackborn/posts/FXCCYxepsDU

  • Dianne刚刚回复说_if Content Provider是存储库模式的框架实现,它不是故意的.:)它的创建是为了在包含应用程序的数据(如联系人,媒体等)和其他想要访问该数据的应用程序之间提供操作系统仲裁的抽象.因此,它提供的关键设施是命名数据项的方法,这些数据项允许操作系统确定谁拥有数据(通过内容:URI),用于与该命名数据交互的标准API,以及用于操作系统的各种(和众多)工具确定和控制谁有权访问哪些数据_ (4认同)

小智 5

这个问题的荣誉,这是一个很好的观察:)。恕我直言,这不是一个是或不是问题,因为它很笼统,因为与大多数设计模式相关的主题都是如此。答案取决于您考虑的环境:

如果您有一个完全依赖平台的应用程序,也就是说考虑Android生态系统上下文,那么,是的,ContentProvider是Repository模式的实现。这里的论点是,内容提供者旨在解决存储库模式旨在解决的一些相同挑战:

  • 它提供了数据层上的抽象,因此代码不一定取决于存储环境
  • 没有从任何地方直接访问数据。您可以将所有SQL查询(或其他任何查询)放在一个地方。当我第一次将ContentProvider实现为菜鸟时,这就像在给我一个启示,我的代码看起来很干净,更改的舒适程度如何
  • 集中数据并在多个客户端(其他应用程序,您已经知道的搜索小部件)之间共享数据,并提供数据安全性机制
  • 您绝对可以定义与数据相关的行为(一种方法是使用ContentObserver)
  • 这是强迫您从早期阶段就单元代码/自动化测试进行组织代码的一种好方法

如果将以上所有内容与存储库模式的原理并排放置,则存在一些严重的相似之处。并非所有人都满意,但核心思想是相同的。

现在,考虑在多个环境(即Web,移动,PC)中大规模运行的应用程序,需求将完全改变。这是一个坏主意,因为每个人都建议依赖ContentProvider作为设计模式。它本身不一定是一个坏主意,但是必须实现一种设计模式,以便其他人可以尽快理解您的代码。您会看到,即使在这里,每个人都建议ContentProvider的通用用法:作为数据源,或者某种程度上依赖于平台。因此,如果您以已知的目的将实现强加于组件之上,事情可能会变得很不清楚。以经典模式组织代码会更好。

tl; dr; 如果您的应用程序在Android设备上是孤立的,则可以肯定地将这两个概念合并在一起。如果您的应用程序被大规模使用,那么在多个平台上使用它会更干净,从而以经典方式组织代码。