Modèle de duplication de classe?

11

Je travaille actuellement en tant que développeur solo sur mon projet actuel. J'ai hérité du projet d'un autre développeur, qui a depuis quitté l'entreprise. Il s'agit d'une application Web de style modèle-vue-contrôleur en C #. Il utilise Entity Framework pour le mappage relationnel des objets. Et il existe deux ensembles de classes différents pour les types dans le modèle de domaine. Un ensemble est utilisé pour interagir avec l'ORM et l'autre est utilisé comme modèle dans le système MVC. Par exemple, il peut y avoir deux classes comme suit:

public class Order{
    int ID{get;set;}
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
}

et

public class OrderModel{
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
    public OrderModel( Order from){
        this.Customer= from.Customer;
        // copy all the properties over individually
    }
    public Order ToOrder(){
        Order result =new Order();
        result.Customer = this.Customer;
        // copy all the properties over individually
    }

}

Je peux penser à plusieurs inconvénients de cette approche (plus d'endroits pour changer le code si quelque chose change, plus d'objets assis dans la mémoire, plus de temps passé à copier des données), mais je ne suis pas vraiment sûr des avantages. Plus de flexibilité pour les classes modèles, je suppose? Mais je pourrais obtenir cela en sous-classant également les classes d'entités. Mon inclination serait de fusionner ces deux groupes de classes, ou peut-être que les classes modèles soient des sous-classes des classes d'entités. Alors est-ce que je manque quelque chose d'important ici? Est-ce un modèle de conception commun dont je ne suis pas au courant? Y a-t-il de bonnes raisons de ne pas passer par le refactoriste que je songe?

MISE À JOUR

Certaines des réponses ici me font réaliser que ma description initiale du projet manquait de détails importants. Il existe également un troisième groupe de classes dans le projet: les classes de modèle de page. Ce sont eux qui sont réellement utilisés comme modèle de support de la page. Ils contiennent également des informations spécifiques à l'interface utilisateur et ne seraient pas stockées avec une commande dans la base de données. Un exemple de classe de modèle de page peut être:

public class EditOrderPagelModel
{
    public OrderModel Order{get;set;}
    public DateTime EarliestDeliveryDate{get;set;}
    public DateTime LatestAllowedDeliveryDate{get;set;}
}

Je vois totalement l'utilité de ce troisième groupe distinct ici, et je n'ai pas l'intention de le fusionner avec autre chose (bien que je puisse le renommer).

Les classes du groupe de modèles sont également actuellement utilisées par l'API de l'application, que je serais également intéressé à entendre si c'est une bonne idée.

Je dois également mentionner que le client représenté ici sous forme de chaîne devait simplifier l'exemple, non pas parce qu'il est en fait représenté de cette façon dans le système. Le système réel a le client comme étant un type distinct dans le modèle de domaine, avec ses propres propriétés

Robert Ricketts
la source
1
"J'ai hérité du projet d'un autre développeur, qui a depuis quitté l'entreprise" il existe un site dédié aux histoires qui commencent ainsi .
Concernant votre mise à jour: cela semble trop d'indirection pour pas assez d'avantages.
Robert Harvey
@RobertHarvey De quoi parlez-vous comme étant trop d'indirection?
Robert Ricketts
1
Le OrderModel. OrderViewModel (ce que vous appelez probablement EditOrderPageModel) est une indirection suffisante; voir ma réponse ci-dessous.
Robert Harvey
@RobertHarvey Cela ressemble à la réponse à la question que j'essayais de poser
Robert Ricketts

Réponses:

16

Alors est-ce que je manque quelque chose d'important ici?

OUI

Bien que ceux-ci ressemblent à la même chose et représentent la même chose dans le domaine, ils ne sont pas les mêmes objets (OOP).

La première est Ordercelle connue par la partie stockage de données du code. L'autre est Ordercelui connu par l'interface utilisateur. Bien que ce soit une coïncidence agréable que ces classes aient les mêmes propriétés, elles ne sont pas garanties .

Ou pour l'examiner sous un autre angle, considérons le principe de responsabilité unique. Le principal facteur de motivation ici est qu '"une classe a une raison de changer". Surtout lorsque vous traitez avec des frameworks omniprésents comme un framework ORM et / ou UI, vos objets doivent être bien isolés car les modifications apportées au framework entraîneront souvent le changement de vos entités.

En liant vos entités aux deux cadres, vous ne faites qu'empirer les choses.

Telastyn
la source
7

Le but du modèle de vue est de fournir le découplage de deux manières: en fournissant des données sous la forme requise par la vue (indépendamment du modèle) et (en particulier dans MVVM) en repoussant tout ou partie de la logique de la vue depuis la vue. au modèle de vue.

Dans un modèle entièrement normalisé, le client ne serait pas représenté dans le modèle par une chaîne, mais plutôt par un ID. Le modèle, s'il a besoin du nom du client, rechercherait le nom dans la table Customers, en utilisant le CustomerID trouvé dans la table Orders.

Un modèle de vue, en revanche, peut être plus intéressé par Namele client, pas par l'ID. Il n'a pas besoin de l'ID, sauf si le client est mis à jour dans le modèle.

En outre, le modèle de vue peut prendre et prend généralement une forme différente (sauf s'il s'agit de CRUD pur ). Un objet Modèle de vue de facture peut avoir un nom de client, des adresses et des éléments de campagne, mais le modèle contient des clients et des descriptions.

En d'autres termes, les factures ne sont normalement pas stockées de la même manière qu'elles sont affichées.

Robert Harvey
la source
3

À certains égards, vous avez raison: ils se réfèrent tous deux au même objet de domaine, qui est appelé order. Mais vous vous trompez dans la mesure où il ne s'agit pas d'une véritable duplication qu'une représentation différente - issue de finalités différentes. Si vous souhaitez conserver votre commande, vous souhaitez par exemple accéder à chaque colonne. Mais vous ne voulez pas divulguer cela à l'extérieur. En plus de pousser des Entités entières à travers le fil n'est pas ce que vous voulez vraiment. Je connais des cas, où une collection de Mo a ordersété expédiée au client où quelques ko suffiraient.

Pour faire une histoire courte - voici les principales raisons pour lesquelles vous voulez le faire:

1) Encapsulation - Masquage des informations de votre application

2) Les petits modèles sont rapides à sérialiser et faciles à transmettre

3) Ils servent à des fins différentes, bien qu'ils se chevauchent, et par conséquent, il devrait y avoir des objets différents.

4) Parfois, il est logique d'avoir pour différentes requêtes des objets de valeur différents . Je ne sais pas comment c'est en C #, mais en Java il est possible d'utiliser un POJO pour collecter les résultats. Si j'en ai besoin d'un seul, orderj'utilise Order -Entity, mais si je n'ai besoin que de quelques colonnes remplies de quantités de données, utiliser des objets plus petits est plus efficace.

Thomas Junk
la source
De la façon dont le framework d'entité est structuré, les entités sont de vieux objets C #, donc les envoyer sur le câble n'est pas vraiment un problème. Je vois l'utilité de pouvoir envoyer une partie du contenu d'une commande dans certaines circonstances.
Robert Ricketts,
Un ou un tas n'est pas le problème comme je l'ai dit. Mais il y a des cas où vous avez des Mo de données, ce qui ralentit votre temps de chargement. Pensez aux temps de chargement mobiles
Thomas Junk
0

(Avertissement: je ne l'ai vu qu'être utilisé de cette façon. J'aurais peut-être mal compris le véritable objectif de le faire. Traitez cette réponse avec suspicion.)

Voici un autre bit manquant: la conversion entre Orderet OrderModel.

La Orderclasse est liée à votre ORM, tandis que la OrderModelest liée à la conception de votre modèle d'affichage.

En règle générale, les deux méthodes seront fournies "de manière magique" (parfois appelées DI, IoC, MVVM ou encore autre chose). Autrement dit, au lieu d'implémenter ces deux méthodes de conversion dans le cadre de OrderModel, elles appartiendront plutôt à une classe dédiée à l'inter-conversion de ces choses; cette classe sera en quelque sorte enregistrée avec le framework afin que le framework puisse facilement le rechercher.

public class OrderModel {
    ...
    public OrderModel(Order from) { ... }
    public Order ToOrder() { ... }
}

La raison en est que chaque fois qu'il y a un croisement de OrderModelvers Orderou vice versa, vous savez que certaines données doivent avoir été chargées ou enregistrées dans ORM.

rwong
la source
Si je garde les deux, je voudrais vraiment les configurer pour que la conversion soit plus automatisée
Robert Ricketts
@RobertRicketts La chose que j'ai vue ressemble à ceci: IMvxValueConverter . Bien que j'aie peut-être mal compris son objectif.
2015