web-dev-qa-db-fra.com

Une classe DTO ayant une ArrayList de son propre type - est-ce considéré comme une bonne conception?

Je suis tombé sur un cours DTO comme celui ci-dessous.

class PersonDTO {
   private String firstName;
   private String middleName;
   private String lastName;
   private String dob;

   // some 50 fields more
   private List<PersonDTO> persons;

   //getters and setter
}

Question 1: Est-ce une bonne pratique d'avoir des classes DTO aussi énormes avec autant de propriétés?

Question 2: Est-ce une bonne pratique d'avoir une classe DTO ayant une ArrayList de son propre type? Cela ne provoquerait-il pas une référence circulaire?

Mise à jour:

Je travaille pour un produit dans le domaine de la santé. Le cas d'utilisation consiste à générer un rapport de tous les utilisateurs du système. Outre la liste des utilisateurs, le rapport doit également afficher les informations récapitulatives. Les données doivent être sérialisées, car l'interface utilisateur attend une réponse JSON.

Vous trouverez ci-dessous le DTO réel que je prenais.

Une méthode a été écrite dans la classe DAO, qui renvoie un objet UserDTO. Cet objet UserDTO qui est retourné .. se compose d'une liste de tous les utilisateurs et de quelques informations résumées comme: nombre total de médecins, nombre total d'infirmières, etc.

class UserDTO{
    private String userID; //This is unique

    private String firstName;
    private String middleName;
    private String lastName;
    private String dob;

    private String userType; // value can be DT(for doctor), ST(for nurse), 

    private int totalDoctorCount; //Holds the total number of UserDTOs with userType value set to DT in the ArrayList of users.

    private int totalNurseCount; //Holds the total number of UserDTOs with userType value set to ST in the ArrayList of users.


    // there are some 40 more properties.

    private List<UserDTO> users;

        //In this class there are also properties like below... 

        private String primaryAddrStreetName;
        private String primaryAddrCity;
        private String primaryAddrState;
        private String primaryAddrCountry;
        private String primaryAddrZipcode;

        private String billingAddrStreetName;
        private String billingAddrCity;
        private String billingAddrState;
        private String billingAddrCountry;
        private String billingAddrZipcode;

        private String shippingAddrStreetName;
        private String shippingAddrCity;
        private String shippingAddrState;
        private String shippingAddrCountry;
        private String shippingAddrZipcode;
}

Question 3: Cette conception DTO est-elle adaptée à ce cas d'utilisation? sinon, que suggéreriez-vous?

Question 4: L'utilisateur a plusieurs adresses. Mais, les détails de l'adresse ne devraient-ils pas être dans sa propre classe (quelque chose comme UserAddressDTO) et ensuite nous devrions ajouter un tableau/ArrayList de UserAddressDTO dans UserDTO ??

Question 5: Veuillez également donner un aperçu de la manière dont ce type de DTO affecterait la mémoire JVM. Le rapport récupérerait des milliers d'enregistrements.

6
madan

Question 1: énorme DTO

Le objectif d'un DTO est de transférer des données entre processus ou couches afin de réduire le nombre d'appels de méthode.

Il n'est donc pas choquant d'avoir une énorme liste de domaines dans une telle structure. L'alternative est d'implémenter le DTO avec une structure plus complexe, comme par exemple un ResultSet (ou un dataset dans un contexte non jaa), qui regroupe différentes entités en un seul objet. L'inconvénient est alors que la construction d'un tel objet et l'accès à ses éléments prennent plus de temps, qu'avec votre structure plate (et laide, mais efficace).

Pour info, ici vous trouverez quelques avantages et responsabilités du DTO.

Question 2 (mise à jour après votre modification) **

Avoir une liste de PersonDTO dans le PersonDTO signifie que le DTO a une structure récursive (par exemple comme un arbre). Il ne s'agit donc pas seulement de données plates d'un Person, mais en fait d'un Person et d'un groupe de Persons connexes. La structure des données est récursive.

Cela n'implique pas une référence circulaire, mais vous avez raison, un soin supplémentaire est nécessaire. Tout dépend de la structure de données d'origine:

  1. Si les objets d'origine forment une hiérarchie (par exemple, un gestionnaire et ses employés, certains d'entre eux étant eux-mêmes des gestionnaires ayant leurs propres employés), il n'y a aucun problème à créer le DTO. Pareil si c'est une autre forme de graphe acyclique.
  2. Si les objets originaux forment un graphique avec des cycles possibles (par exemple, une personne et ses amis), il y a un risque de cycle pour toujours lors de la construction du DTO. Vous pouvez utiliser une détection de cycle pour éviter de transférer plusieurs fois le même objet. Cependant, cela signifierait la perte de certaines relations dans le transfert. Dans ce cas, ce DTO serait une mauvaise conception.

Notez également qu'une même personne pourrait apparaître plusieurs fois dans le DTO, dans différentes listes (par exemple un employé à temps partiel travaillant pour deux départements). Malheureusement, pendant le transfert de données, si les objets sont désérialisés pour être transférés sur le net puis sérialisés à nouveau, l'identité de l'objet (que vous avez dans Java avec la même référence) peut se perdre, et vous vous retrouverez avec deux objets différents répliquant les mêmes données. Du côté de la réception, vous devrez donc faire très attention: vous devez décider si vous détecterez une telle identité d'objets ou si vous supposez que toutes les personnes seront toujours différentes.

Conclusion: cette structure pourrait être correcte, mais elle nécessite quelques informations sur les objets de domaine d'origine pour confirmer.

Alternative: Une structure DTO beaucoup plus sûre serait de donner à chaque personne un identifiant et d'organiser le DTO comme une collection de personnes avec leur identifiant. La liste de PersonDTO pourrait alors être remplacée par une liste d'id. C'est beaucoup plus flexible, car cela fonctionnerait pour tous les types de graphiques de personne et sans besoin de détection d'identité.

Question 3 et votre dernière modification

Si le but de cette structure DTO est seulement de fournir une liste d'utilisateurs non liés , elle doit avoir la structure:

class UserDTO {             // Only properties of a single user
    private String userID; 
    private String firstName;
    ...
    private String userType;  
};
class UserListDTO {         // Only properties of the list as a whole
    private int totalDoctorCount; 
    ...
    private List<UserDTO> users;
};

Comme vous le voyez, une conception DTO appropriée est souvent liée à une multiplication de classes DTO. C'est un sujet bien connu comme vous pouvez le vérifier dans les inconvénients exposés dans cet article ici .

Gonfler un DTO unique pour combiner les deux couches en une seule classe est un raccourci qui n'est pas conforme au principe de responsabilité unique . C'est aussi ambigu: faut-il ajouter l'utilisateur le plus important au groupe? Est-il alors compté dans les statistiques? Les utilisateurs de la liste doivent-ils toujours avoir une liste vide? Devront-ils fournir des statistiques sur eux-mêmes? Que faire si un utilisateur de la liste contient des utilisateurs inattendus dans sa liste? Toutes ces questions et traitements supplémentaires, juste pour éviter une classe supplémentaire ...

Dans ce cas, c'est une mauvaise conception.

6
Christophe

Wikipedia

Un objet de transfert de données (DTO) est un objet qui transporte des données entre les processus. La motivation de son utilisation est que la communication entre les processus se fait généralement en recourant à des interfaces distantes (par exemple des services Web), où chaque appel est une opération coûteuse. Étant donné que la majorité du coût de chaque appel est liée au temps d'aller-retour entre le client et le serveur, une façon de réduire le nombre d'appels consiste à utiliser un objet (le DTO) qui regroupe les données qui auraient été transférées. par plusieurs appels, mais qui est servi par un seul appel

Selon la définition ci-dessus, si votre DTO est adressé pour réduire le nombre d'appels entre les processus, nous pourrions discuter de la taille du DTO mais pas de son adéquation. D'un autre côté, si le DTO ne répond à aucune stratégie de communication, la réponse probable est: veuillez revoir votre conception. Celui qui va consommer le DTO aura du mal à explorer, analyser et extraire des données d'une telle monstruosité.

En ce qui concerne la deuxième question, il pourrait y avoir une référence circulaire si, pour une raison quelconque, le même DTO est référencé quelque part dans la liste ou référencé par l'une des listes internes du DTO. Etc.


Mise à jour après votre modification

@Christophe a déjà dit ce que nous pensons tous (plus ou moins). si UserDTO contient toutes les données requises pour le rapport, vous lui avez fourni trop de responsabilités.

Considérez les 2 options suivantes:

  • modélisation de la ressource Web ReportDTO composée par d'autres DTO et ses propres attributs locaux.

  • faire plus d'appels au serveur. Nous savons que le but du DTO est le contraire, mais ne devenons pas fou, réduire 50 appels à 1 est excellent, mais ne le faites pas au risque d'implémenter le One DTO ( un DTO pour les amener tous, et dans les ténèbres les lier). Réduire le nombre d'appels de 50 à 5 est toujours une grande amélioration.

1
Laiv

Question 1: Est-ce une bonne pratique d'avoir des classes DTO aussi énormes avec autant de propriétés?

Comme d'autres l'ont mentionné, un DTO peut certainement être aussi important si son consommateur en a besoin. Il est difficile de dire si un DTO si important est une bonne pratique car le DTO sert un objectif si étroit qu'il n'y a pas de réelle variation de "bonne pratique" dans leur utilisation.

Question 2: Est-ce une bonne pratique d'avoir une classe DTO ayant une ArrayList de son propre type? Cela ne provoquerait-il pas une référence circulaire?

En termes de Modélisation, un Person n'est pas composé de plusieurs autres Persons. Un Person peut avoir plusieurs siblings de typePerson. Le point étant un membre appelé persons ne transmet aucune information pertinente sur sa relation avec l'objet. D'un autre côté, il est évident qu'un membre nommé siblings décrit une relation familière entre ces instances Person.

1
MetaFight

Cinquante propriétés semblent un peu excessives. Vous devriez essayer de les garder aussi fin que possible. N'incluez que ce dont vous avez besoin. Bien que j'en ai vu qui sont de 10 à 15 propriétés.

Et en ce qui concerne le ArrayList, vous pouvez en avoir un sans problème. La référence circulaire ne se produirait que si, pour une raison quelconque, vous stockiez l'élément réel qui fait le travail comme référence ou quelque chose de bizarre comme ça.

0
Rhys Johns