40 votes

Le fournisseur de contenu est-il une implémentation du modèle de référentiel ?

Modèle de référentiel est défini par Hieatt et Rob Mee comme modèle de conception qui assure la médiation entre les couches de mappage de domaine et de données en utilisant une interface de type collection pour accéder aux objets de domaine. .

MSDN Repository Pattern

Il s'agit essentiellement d'abstraire un ou plusieurs périphériques d'E/S (nuage, disque, base de données, etc.) dans une interface commune de type collection où vous pouvez lire, écrire, rechercher et supprimer des données .

Sur L'architecture propre d'Android de Fernando Cejas Dans la couche domaine, toutes les données nécessaires à l'application proviennent de cette couche par le biais d'une implémentation de référentiel (l'interface se trouve dans la couche domaine) qui utilise un modèle de référentiel avec une stratégie qui, par le biais d'une usine, choisit différentes sources de données en fonction de certaines conditions.

Content Provider

Cependant, comme l'a souligné le professeur Douglas Schmidt à l'adresse Coursera , fournisseur de contenu gère et médiatise l'accès à un dépôt central de données pour une ou plusieurs applications.

Content Provider

Dans le livre Programmation Android les fournisseurs de contenu sont utilisés comme façade d'un service Web RESTful . Cette approche a été initialement présentée par Virgil Dobjanschi lors de la Google I/O 2010 .

Ainsi, au lieu d'utiliser des fournisseurs de contenu pour accéder à la base de données SQLite locale pourquoi ne pas l'utiliser comme le modèle de référentiel lui-même ?

enter image description here

27voto

sockeqwe Points 1672

Essayons de comparer la définition du Repository Pattern du livre "Patterns of Enterprise Application Architecture" par Martin Fowler (avec Dave Rice, Matthew Foemmel, Edward Hieatt, Robert Mee et Randy Stafford) avec ce que nous savons sur ContentProviders .

Le livre dit :

Un référentiel sert de médiateur entre le domaine et les couches de mappage des données en utilisant une interface de type collection pour accéder aux objets du domaine.

Le point important est accessing domain objects . À première vue, il semble donc que le modèle de référentiel ne soit destiné qu'à l'accès aux données (interrogation). Avec un ContentProvider Cependant, vous pouvez non seulement accéder (lire) aux données, mais aussi les insérer, les mettre à jour ou les supprimer. Cependant, le livre dit :

Des objets peuvent être ajoutés et retirés du référentiel, comme ils le peuvent d'une simple collection d'objets, et le code de mapping encapsulé dans le référentiel par le référentiel effectuera les opérations appropriées en coulisse. scènes.

Donc, oui, Repository et ContentProvider semblent offrir les mêmes opérations (point de vue de très haut niveau), bien que le livre indique explicitement que simple collection of objects ce qui n'est pas vrai pour ContentProvider puisqu'il nécessite un système Android spécifique ContentValues y Cursor du client (qui utilise un certain ContentProvider ) pour interagir avec.

De plus, le livre mentionne domain objects y data mapping layers :

Un référentiel sert de médiateur entre le domaine et les couches de mappage des données.

y

Sous la couverture, Repository combine le mappage de métadonnées (329) avec un objet de requête (316). Metadata Mapping contient les détails du mapping objet-relationnel dans les métadonnées.

Le mappage de métadonnées signifie essentiellement comment mapper une colonne SQL à un champ de classe java.

Comme déjà mentionné, ContentProvider renvoie un Cursor à partir d'une opération query(). De mon point de vue, un curseur n'est pas un objet de domaine. De plus, le mappage du curseur à l'objet de domaine doit être effectué par le client (qui utilise un ContentProvider). De mon point de vue, le mappage des données est donc complètement absent du ContentProvider. En outre, le client peut être amené à utiliser un ContentResolver aussi pour obtenir l'objet du domaine (données). À mon avis, cette API est en contradiction flagrante avec la définition du livre :

Repository soutient également l'objectif de réaliser une séparation nette et une dépendance unidirectionnelle entre les couches de mappage de domaine et de données.

Ensuite, nous allons nous concentrer sur l'idée principale du modèle de référentiel :

Dans un grand système avec de nombreux types d'objets de domaine et de nombreuses possibilités d'utilisation. requêtes possibles, Repository réduit la quantité de code nécessaire pour gérer toutes les l'interrogation qui se passe. Repository promeut le pattern Specification (sous la forme de l'objet critère dans les exemples ici), qui encapsule la requête à effectuer d'une manière purement orientée objet. objet. Par conséquent, tout le code pour la mise en place d'un objet de requête dans des applications spécifiques de type spécifiques peut être supprimé. Les clients ne doivent jamais penser en SQL et peuvent écrire code purement en termes d'objets.

ContentProvider requiert un URI (chaîne de caractères). Il ne s'agit donc pas vraiment d'une "méthode orientée objet". Un ContentProvider peut également avoir besoin d'un projection et un where-clause .

On pourrait donc dire qu'une chaîne URI est une sorte d'encapsulation puisque le client peut utiliser cette chaîne au lieu d'écrire un code SQL spécifique, par exemple :

Avec un Référentiel, le code client construit les critères et passe ensuite à au référentiel, lui demandant de sélectionner les objets qui correspondent à ces critères. correspondent. Du point de vue du code client, il n'y a pas de notion d'"exécution" de la requête. l'"exécution" d'une requête ; il s'agit plutôt de la sélection d'objets appropriés par la "satisfaction" de la spécification de la requête.

L'utilisation d'un URI (chaîne de caractères) par ContentProvider ne semble pas contredire cette définition, mais il manque toujours la méthode orientée objet mise en avant. De plus, les chaînes ne sont pas des objets de critères réutilisables qui peuvent être réutilisés d'une manière générale pour composer la spécification des critères afin de "réduire la quantité de code nécessaire pour traiter toutes les requêtes qui ont lieu."

Par exemple, pour trouver des objets de type personne par leur nom, nous créons d'abord un objet de type critère. en définissant chaque critère individuel comme suit criteria.equals(Person.LAST_NAME, "Fowler"), et criteria.like(Person.FIRST_NAME, "M"). Ensuite, nous invoquons repository.matching(criteria) pour retourner une liste d'objets de domaine représentant les personnes ayant le nom de famille Fowler et un prénom commençant par M.

Comme vous l'avez déjà dit (dans votre question), Repository est également utile pour cacher différentes sources de données comme un détail d'implémentation que le client ne connaît pas. Ceci est vrai pour les ContentProviders et spécifié dans le livre :

La source d'objets pour le référentiel ne peut pas être une base de données relationnelle ce qui n'est pas grave puisque le Référentiel se prête assez facilement au remplacement du composant de cartographie des données par des objets de stratégie spécialisés. Pour cette raison, il peut être particulièrement utile dans les systèmes avec de multiples schémas de base de données ou de sources pour les objets du domaine, ainsi que pendant les tests lorsque l'utilisation d'objets exclusivement en mémoire est souhaitable pour la rapidité.

y

Parce que l'interface de Repository protège la couche de domaine de toute sensibilisation. de la source de données, nous pouvons refacturer l'implémentation de la requête à l'intérieur du Repository sans modifier les appels des clients. En effet, le code du domaine n'a pas besoin de se soucier de la source ou de la destination des objets du domaine.


Donc, pour conclure : Certaines définitions du livre de Martin Fowler et al. correspondent à l'API d'un ContentProvider (si l'on ignore le fait que le livre met l'accent sur l'orientation objet) :

  • Masque le fait qu'un référentiel / ContentProvider a différentes sources de données.
  • Le client ne doit jamais écrire une requête dans un DSL spécifique à la source de données comme SQL. C'est vrai pour ContentProvider si nous considérons que l'URI n'est pas spécifique à une source de données.
  • Les deux, Repository et ContentProvider, ont le même ensemble d'opérations de "haut niveau" : lire, insérer, mettre à jour et supprimer des données (si vous ignorez le fait que Fowler parle beaucoup d'orientation objet et de collection d'objets alors que ContentProvider utilise Cursor et ContentValues).

Cependant, ContentProvider passe vraiment à côté de certains points clés du modèle de référentiel tel que décrit dans le livre :

  • Puisque ContentProvider utilise l'URI (également une chaîne de caractères pour la clause where), un client ne peut pas réutiliser les objets Matching Criteria. C'est un point important à noter. Le livre dit clairement que le modèle de référentiel est utile "Dans un grand système avec de nombreux types d'objets de domaine et de nombreuses requêtes possibles, le référentiel réduit la quantité de code nécessaire pour traiter toutes les requêtes qui se produisent". Malheureusement, ContentProvider n'a pas d'objets Criteria tels que criteria.equals(Person.LAST_NAME, "Fowler") qui peuvent être réutilisés et utilisés pour composer des critères de correspondance (puisque vous devez utiliser des chaînes de caractères).
  • ContentProvider manque entièrement le mappage des données car il renvoie un Cursor . C'est très mauvais car un client (qui utilise un ContentProvider pour accéder aux données) doit faire le mappage de Cursor à l'objet de domaine. De plus, cela signifie que le client a connaissance des éléments internes du référentiel, comme le nom des colonnes. "Le référentiel peut être un bon mécanisme pour améliorer la lisibilité et la clarté du code qui utilise intensivement les requêtes." Ce n'est certainement pas vrai pour les ContentProviders.

Donc non, un ContentProvider n'est pas une implémentation du modèle Repository. tel que défini dans le livre "Patterns of Enterprise Application Architecture" parce qu'il manque au moins deux choses essentielles que j'ai soulignées ci-dessus.

Veuillez également noter que, comme le nom du livre le suggère déjà, le modèle de référentiel est destiné à être utilisé pour les applications d'entreprise où vous effectuez de nombreuses requêtes.

Les développeurs Android ont tendance à utiliser le terme "Repository pattern" mais ne veulent pas vraiment dire le pattern "original" décrit par Fowler et al. (haute réutilisabilité des Criterias pour les requêtes) mais plutôt une interface pour cacher la source de données sous-jacente (SQL, Cloud, peu importe) et le mapping des objets du domaine.

Plus d'informations ici : http://hannesdorfmann.com/Android/evolution-of-the-repository-pattern

7voto

k3b Points 5381

Réponse courte : un fournisseur de contenu est un source de données et non un dépôt.

Le but de SQL-Database/Android-Contentproviders/Repositories est de créer/lire/mettre à jour/supprimer/retrouver des données.

Les référentiels fonctionnent généralement sur des classes java de haut niveau, spécifiques aux bus (comme Customer, Order, Product, ....). tandis que les bases de données SQL et les fournisseurs de contenu Android opèrent sur des tables de bas niveau, des lignes et des colonnes comme une classe Java. source de données .

Étant donné qu'une base de données SQL n'est pas un référentiel, une base de données SQL ne peut pas être utilisée. Android-Contentprovider n'est pas un référentiel aussi.

Mais vous pouvez mettre en œuvre un référentiel en utilisant un fournisseur de contenu sous-jacent.

6voto

nglauber Points 108

Je vais mentionner Dianne Hackborn (de l'équipe Android Framework) pour donner mon avis.

ContentProvider

Enfin, le ContentProvider est un outil assez spécialisé permettant de publier les données d'une application vers d'autres endroits. Les gens les considèrent généralement comme une abstraction sur une base de données, parce qu'il y a beaucoup d'API et de support intégré pour ce cas commun... mais du point de vue de la conception du système, ce n'est pas leur but.

Pour le système, il s'agit d'un point d'entrée dans une application permettant de publier des éléments de données nommés, identifiés par un schéma URI. Ainsi, une application peut décider de la manière dont elle souhaite faire correspondre les données qu'elle contient à un espace de noms URI, en transmettant ces URI à d'autres entités qui peuvent à leur tour les utiliser pour accéder aux données. Cela permet au système de faire quelques choses de particulier dans la gestion d'une application :

- La distribution d'un URI n'exige pas que l'application reste en marche, ce qui fait qu'il peut se passer n'importe quoi, même si l'application propriétaire est morte. Ce n'est qu'au moment où quelqu'un dit au système "Donne-moi les données de cette URI" qu'il doit s'assurer que l'application propriétaire de ces données est en cours d'exécution, afin de pouvoir demander à l'application de récupérer et de renvoyer les données.

- Ces URI fournissent également un important modèle de sécurité à grain fin. Par exemple, une application peut placer l'URI d'une image qu'elle possède dans le presse-papiers, mais laisser son fournisseur de contenu verrouillé afin que personne ne puisse y accéder librement. Lorsqu'une autre application retire cette URI du presse-papiers, le système peut lui accorder une "autorisation d'accès à l'URI" temporaire afin qu'elle soit autorisée à accéder aux données de cette URI uniquement, mais à rien d'autre dans l'application.

Ce dont nous nous moquons :

La façon dont vous implémentez la gestion des données derrière un fournisseur de contenu n'a pas vraiment d'importance ; si vous n'avez pas besoin de données structurées dans une base de données SQLite, n'utilisez pas SQLite. Par exemple, la classe d'aide FileProvider est un moyen simple de rendre les fichiers bruts de votre application disponibles par le biais d'un fournisseur de contenu.

En outre, si vous ne publiez pas de données de votre application pour que d'autres puissent les utiliser, il n'est pas nécessaire d'utiliser un fournisseur de contenu. Il est vrai qu'en raison des diverses aides construites autour des fournisseurs de contenu, il peut s'agir d'un moyen facile de placer des données dans une base de données SQLite et de les utiliser pour alimenter des éléments d'interface utilisateur comme une ListView. Mais si l'un de ces éléments rend plus difficile ce que vous essayez de faire, alors n'hésitez pas à ne pas l'utiliser et à utiliser un modèle de données plus approprié pour votre application.

Texte complet ici : https://plus.google.com/+DianneHackborn/posts/FXCCYxepsDU

4 votes

Dianne vient de répondre que si le Content Provider est l'implémentation du framework du pattern repository, ce n'était pas intentionnel :) Il a été créé pour fournir une abstraction arbitrée par le système d'exploitation entre une application contenant des données (comme les contacts, les médias, etc.) et d'autres applications qui veulent accéder à ces données. Ainsi, les principales fonctionnalités qu'il fournit sont un moyen de nommer les éléments de données qui permet au système d'exploitation de déterminer qui possède les données (via le contenu : URI), une API standard pour interagir avec ces données nommées, et diverses (et nombreuses) fonctionnalités pour le système d'exploitation de déterminer et de contrôler qui a accès à quelles données.

5voto

Bravo pour la question, c'est une belle observation :). À mon avis, il ne s'agit pas d'une question à réponse positive ou négative, car elle est assez générale, comme la plupart des sujets liés aux patrons de conception. La réponse dépend du contexte que vous prenez en compte :

Si vous avez une application qui repose entièrement sur la plateforme, cela signifie que vous devez prendre en compte les éléments suivants seulement le contexte de l'écosystème Android, alors Oui, le ContentProvider EST une implémentation du modèle Repository. . L'argument ici est que le fournisseur de contenu était conçu pour résoudre certains des problèmes que les modèles de référentiel visent à résoudre :

  • Il fournit une abstraction sur la couche de données, de sorte que le code ne dépend pas nécessairement de l'environnement de stockage.
  • Pas d'accès direct aux données de partout. Vous pouvez placer toutes vos requêtes SQL (ou autre) en un seul endroit. Lorsque j'ai implémenté pour la première fois un ContentProvider en tant que novice, j'ai eu la révélation de la propreté de mon code et de l'aisance avec laquelle je peux effectuer des modifications.
  • Centralise les données et les partage entre plusieurs clients (d'autres applications, un widget de recherche comme vous le savez déjà) et fournit un mécanisme pour la sécurité des données.
  • Il est tout à fait possible de définir un comportement lié aux données (par exemple, en utilisant ContentObserver).
  • C'est un bon moyen de vous obliger, dès les premières étapes, à organiser votre code en tenant compte des tests unitaires et des tests automatisés.

Si vous mettez tout ce qui précède côte à côte avec les principes du modèle de référentiel, il y a de sérieuses similitudes. Elles ne sont pas toutes satisfaites, mais les idées de base sont les mêmes.

Maintenant, si l'on considère qu'une application fonctionnant à plus grande échelle en environnements multiples (c.-à-d. web, mobile, PC), les exigences changent complètement. Comme tout le monde l'a suggéré, c'est une mauvaise idée de s'appuyer sur le ContentProvider comme modèle de conception. . Ce n'est pas nécessairement une mauvaise idée en soi, mais un modèle de conception doit être mis en œuvre pour que les autres puissent comprendre votre code le plus rapidement possible. Vous voyez, même ici, tout le monde a suggéré une utilisation commune de ContentProvider : comme source de données, ou de toute façon quelque chose qui dépend de la plate-forme. Ainsi, si vous imposez une implémentation au-dessus d'un composant dont l'objectif est connu, les choses peuvent devenir plutôt floues. Il est beaucoup plus agréable d'organiser votre code selon un modèle classique.

tl;dr ; Si votre application est isolée sur votre appareil Android, vous pouvez certainement fusionner les deux concepts. Si votre application est utilisée à plus grande échelle, sur plusieurs plateformes, il est plus propre d'organiser votre code de manière classique.

0 votes

Felicitari pour une belle réponse, Alexandru :)

2voto

rontho Points 331

C'est une question intéressante. Je pense que ma première réponse sera non, Content Provider n'est pas une implémentation du Repository Pattern.

Comme vous l'avez mentionné, le modèle de référentiel est destiné à séparer la logique commerciale (domaine) de la couche de données. Cette approche vous permet de créer des tests unitaires pour votre logique métier (le domaine ne devrait donc pas du tout dépendre d'Android). En utilisant un fournisseur de contenu, vous devrez avoir une sorte d'objets Android dans votre domaine.

Vous pourriez imaginer un moyen de cacher la logique du fournisseur de contenu derrière une interface, mais vous perdriez la plupart des avantages qu'un fournisseur de contenu vous permet de faire.

Si l'architecture Android vous intéresse, je vous recommande de jeter un coup d'œil à ce projet Github. Architecture propre d'Android . Vous trouverez une façon agréable de séparer votre présentation, votre domaine et votre couche de données, et la communication entre le domaine et les données est effectuée en utilisant un modèle de référentiel.

J'espère que cela vous aidera !

0 votes

En utilisant des fournisseurs de contenu, au lieu de s'appuyer sur des interfaces explicitement déclarées, les cas d'affaires seraient mis en œuvre avec des résolveurs de contenu. À mon avis, l'utilisation du fournisseur de contenu comme façade permet d'obtenir une collection observable/itérable, MAIS certainement pas aussi conviviale que RxJava Observable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X