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