Identifiant vs objet de domaine comme paramètre de méthode


10

Existe-t-il des arguments objectifs pour ou contre l'utilisation d'objets par rapport à l'ID unique comme paramètres de méthode / fonction? (et membres d'autres objets?). Spécialement dans le contexte des langages typés statiquement (C # / Java / Scala)

Avantages de l'objet lui-même:

  • Plus d'appels sécurisés. Avec les identifiants, il existe un risque de mauvais ordre des arguments. Cela peut être atténué cependant en conservant une «mini» classe pour chaque classe qui ne stocke que l'ID de cette classe.
  • Obtenez une fois de la persévérance, pas besoin de revenir
  • Avec l'ID, si le type d'ID change, disons int -> long, cela nécessiterait un changement généralisé avec possibilité d'erreurs .. (courtsey: https://softwareengineering.stackexchange.com/a/284734/145808 )

Avantages de l'utilisation de l'ID:

  • La plupart du temps, aucun objet réel n'est nécessaire, seul l'identifiant unique le ferait, donc avoir un ID permet d'économiser du temps pour l'obtenir de la persistance.

Un mélange de ces techniques, pour autant que je puisse voir, n'aurait que des inconvénients des deux et des avantages de ni l'un ni l'autre.

Étant donné qu'il s'agit d'un problème concrètement défini, j'espère qu'il y a des réponses objectives et non des types "je pense" ou "j'aime" ... :-).

EDIT: Contexte sur la suggestion d'un commentateur - La seule restriction est une langue typée statiquement. L'application est une application à usage général, bien qu'elle soit également heureuse d'obtenir des réponses basées sur des scénarios d'utilisation spécifiques.

EDIT: Un peu plus de contexte. Disons que j'ai un système de gestion des livres. Mon modèle est:

Book: { id: Int, isbn: String, donatedBy: Member, borrowedBy: Member }
Member: {id: Int, name: String}

Table- wishlist: { isbn: String, memberId: Int}
Table- borrows:  { id: Int, memberId: Int}  

Method: 
isInWishList( book: Book, member: Member ) vs isInWishList( bookId: Int,  memberId: Int)

handleBorrowRequest( memberId: Int, book: Book ){ 
   //the login system doesn't actually get the entire member structure, only the member id. I don't need the full member yet. can run a query to verify that the memberId has less than max allowed books for borrowing. 
}

Pourquoi voudrais-je ne garder que l'ID et non le membre à part entière? Dites que lorsque je reçois des informations sur le livre, j'ai rarement besoin de savoir qui l'a donné ou emprunté. Obtenir leurs informations complètes pour remplir les champs donatedBy et borrowedBy est une exagération.

Bien sûr, ce ne sont que mes points. Je suis sûr que je manque des choses alors je voulais savoir quels sont les points pour / contre.


Pourriez-vous modifier votre question avec un peu plus de contexte? Le scénario n'est pas clair. Je vais deviner que vous parlez d'objets qui sont gérés par un système ORM (comme Hibernate) et que par "mini-classe" vous voulez dire un objet proxy à chargement paresseux.
Darien


Ajouter un commentaire ici (jusqu'à ce que je puisse obtenir suffisamment de contexte pour développer une réponse complète): À moins que vous codiez en dur les valeurs d'ID dans votre code (un grand non-non), vous devrez toujours charger vos valeurs d'ID de quelque part à droite? "La plupart du temps" est également assez vague à
mon humble avis

@hjk Ajout d'un peu plus de contexte, merci. Vous avez un point - vous devez toujours charger les valeurs ID de quelque part. Cependant, il ne s'agit pas de «toucher» la base de données, mais de minimiser l'utilisation et la bande passante de la base de données. Je peux obtenir des identifiants de tous ceux qui veulent emprunter un livre, mais en réalité, obtenir tous leurs détails serait inutile.
0fnt

Il semble que cela dépende de plusieurs choses: (1) si les cas d'utilisation nécessitent les attributs réels (colonnes) ou simplement l'ID, qui dépend de ces cas d'utilisation eux-mêmes; (2) si vous utilisez des techniques de mise en cache ou d'amélioration des performances qui rendraient la requête ID-to-attribut sans effort, et si vos cas d'utilisation seraient suffisamment lourds pour rompre ces schémas de mise en cache.
rwong

Réponses:


8

En fonction de votre environnement et du contexte du problème, la nécessité d'accéder à la base de données peut être atténuée et, par conséquent (IMO), l'argument pour utiliser uniquement les identifiants est perdu.

Par exemple, PHP Doctrine2 ORM a EntityManager::getReferencequi produit un proxy chargé paresseusement à une entité en utilisant l'id fourni; Je ne sais pas combien d'autres ORM font cela, mais cela correspond à peu près à la notion de "mini-classe" que vous mentionnez. Dans la plupart des cas, il s'agit d'une entité entièrement hydratée, vous pouvez donc la faire circuler et l'utiliser comme n'importe quelle autre.

Le seul cas où ce n'est pas le cas, c'est quand on lui donne un identifiant non valide, et dans ce cas, vous voudrez quand même aller dans la base de données pour valider; De plus, pour réduire le coût d'obtention des entités, la plupart des ORM ont une fonctionnalité de mise en cache (premier et deuxième niveau) disponible sous une forme ou une autre.

Une fois que nous avons réduit le besoin et le coût d'aller à la base de données, il nous reste en grande partie avec les avantages d'utiliser l'entité comme paramètre:

  1. Tapez la sécurité.
  2. Moins de connaissances requises par l'appelant. Un développeur 6 mois plus tard ne peut pas transmettre par erreur l'identifiant de la mauvaise entité.
  3. Coût de refactorisation réduit. Si une méthode devait changer pour exiger 2 éléments d'information de l'entité, il n'y a aucun coût à changer l'appelant pour le fournir, en supposant que l'appelant a obtenu l'entité complètement hydratée pour commencer.
  4. Moins de validation requise par méthode. De nombreuses méthodes peuvent supposer que l'entité qui leur est attribuée existe dans la base de données (car elle provient de l'ORM) et n'ont donc plus à valider l'ID.
  5. (Potentiellement) moins d'appels de base de données. Si vous passez des identifiants à 3 méthodes et que chacune doit la valider ou extraire plus de données sur l'entité, cela représente 3 fois plus d'appels de base de données qu'une seule méthode récupérant l'entité et 2 fonctionnant dessus.

Bien sûr, il y a des cas où l'on peut vouloir passer un identifiant uniquement: par exemple lors du franchissement d'une limite de contexte borné (dans la conception pilotée par le domaine). Si nous considérons deux contextes délimités avec des notions différentes de ce qui constitue un compte (par exemple, finances vs relations clients), nous ne pouvons pas passer du compte du contexte A au contexte B. Au lieu de cela, un identifiant de compte (dans la langue du domaine) peut être traversé.


La mise en cache ne s'adapterait pas bien avec plusieurs instances d'application en cours d'exécution. J'ai ma propre mise en cache en place. Les points sont cependant excellents.
0fnt

Je suppose qu'un autre point contre les ID est que l'utilisation d'ID implique d'appeler la persistance en plus de la validation, pour obtenir réellement l'objet aussi.
0fnt

3

Pour ce genre de question, je délègue à la solution qui communique le mieux mon intention en tant que programmeur et facilite le travail de "Future Greg".

L'utilisation de l'objet comme arguments facilite l'obtention de l'appel de méthode dans 6 mois et j'ai oublié les détails de cette application. Pour construire sur votre "est ce livre dans l'exemple de liste de souhaits de cette personne", disons que la isInWishListméthode n'a vraiment besoin que de comparer les identifiants entiers des livres et des membres. En réalité, cette méthode n'a besoin que de deux éléments de données pour faire son travail. Maintenant, prenons un peu de recul par rapport à cette méthode et examinons l'ensemble du système logiciel. Je doute qu'il ne nécessite qu'un identifiant de livre et un identifiant de membre pour fonctionner. Vous tirerez de toute façon le livre complet et le membre de la base de données avant même d'appeler le isInWishListcar il est probable que vous devrez faire quelque chose de plus que d'appeler cette seule méthode. Peut-être que vous le faites, mais la plupart du temps, vous devrez en faire plus.

Compte tenu de cela, vous n'allez pas subir de pénalité de performance en récupérant et en mappant les deux objets dans leur intégralité à partir de la base de données. Théoriquement, vous aurez besoin de moins d'appels de base de données, mais vous constaterez en pratique que ce n'est tout simplement pas vrai. Si vous transmettez ces ID en tant que paramètres dans la chaîne de requête au serveur, alors oui, vous aurez juste besoin de récupérer la liste de souhaits dans la base de données. Mais c'est une vue très étroite de l'ensemble de votre application. Il y aura des scénarios lorsque vous effectuez d'autres opérations en plus de vérifier une liste de souhaits --- par exemple, ajouter un livre à une liste de souhaits. Vous ne souhaitez pas ajouter d'éléments en double.

Optez pour la solution la plus simple à comprendre pour un nouveau programmeur, ou votre futur moi, qui consiste à transmettre les objets Booket Member. Ce n'est qu'après avoir trouvé un problème de performances réel que vous devriez vraiment vous soucier de simplement passer les identifiants.

Ou, nous pourrions nous rappeler que les langages typés statiquement comme Java offrent une surcharge de méthode, vous pouvez donc avoir votre gâteau et le manger aussi:

public boolean isInWishList(int bookId, int memberId) {
    // ...
}

public boolean isInWishList(Book book, Member member) {
    return isInWishList(book.getId(), member.getId());
}

La vraie réponse à cette question est d'écrire les deux méthodes, d'appeler la version entière uniquement à partir de la version fortement typée, et Bob est votre oncle.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.