Padrão de duplicação de classe?

11

Atualmente, estou trabalhando como desenvolvedor solo no meu projeto atual. Eu herdei o projeto de outro desenvolvedor, que deixou a empresa. É um aplicativo da web no estilo model-view-controller em C #. Ele usa o Entity Framework para mapeamento relacional de objetos. E há dois conjuntos diferentes de classes para os tipos no modelo de domínio. Um conjunto é usado para interagir com o ORM e o outro é usado como modelos no sistema MVC. Por exemplo, pode haver duas classes da seguinte maneira:

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

e

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
    }

}

Posso pensar em várias desvantagens dessa abordagem (mais lugares para alterar o código se algo mudar, mais objetos guardados na memória, mais tempo gasto copiando dados), mas não tenho certeza de quais são as vantagens aqui. Mais flexibilidade para as classes de modelos, suponho? Mas eu poderia conseguir isso subclassificando as classes de entidade também. Minha inclinação seria mesclar esses dois grupos de classes ou, possivelmente, fazer com que as classes de modelo sejam subclasses das classes de entidade. Então, estou perdendo algo importante aqui? Esse é um padrão de design comum que não conheço? Existem boas razões para não continuar com o refator que estou pensando?

ATUALIZAR

Algumas das respostas aqui estão me fazendo perceber que minha descrição inicial do projeto carecia de alguns detalhes importantes. Também existe um terceiro grupo de classes no projeto: as classes de modelo de página. Eles são os que realmente estão sendo usados ​​como o modelo de backup da página. Eles também contêm informações específicas da interface do usuário e não seriam armazenadas com um pedido no banco de dados. Um exemplo de classe de modelo de página pode ser:

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

Vejo totalmente a utilidade desse terceiro grupo aqui distinta e não tenho planos de fundi-lo com outra coisa (embora eu possa renomeá-lo).

Atualmente, as classes no grupo de modelos também são usadas pela API do aplicativo, que eu também estaria interessado em ouvir sobre se isso é uma boa ideia.

Também devo mencionar que o cliente que está sendo representado como uma string aqui foi para simplificar o exemplo, não porque ele está sendo representado dessa maneira no sistema. O sistema real tem o cliente como um tipo distinto no modelo de domínio, com suas próprias propriedades

Robert Ricketts
fonte
11
"Herdei o projeto de outro desenvolvedor, que deixou a empresa", existe um site dedicado a histórias que começam dessa maneira .
Em relação à sua atualização: isso parece muito indireto para um benefício insuficiente.
Robert Harvey
@RobertHarvey Do que você está se referindo como sendo muito indireto?
Robert Ricketts
11
O OrderModel. OrderViewModel (ao que você provavelmente está se referindo como EditOrderPageModel) é uma indireção suficiente; veja minha resposta abaixo.
Robert Harvey
@RobertHarvey isso soa como a resposta para a pergunta que eu estava realmente tentando perguntar
Robert Ricketts

Respostas:

16

Então, estou perdendo algo importante aqui?

SIM

Embora pareçam a mesma coisa e representem a mesma coisa no domínio, eles não são os mesmos objetos (OOP).

Um é o Orderconhecido pela parte do código de armazenamento de dados. O outro é o Orderconhecido pela interface do usuário. Embora seja uma coincidência agradável que essas classes tenham as mesmas propriedades, elas não são garantidas .

Ou, para examiná-lo de outra perspectiva, considere o Princípio da Responsabilidade Única. O principal motivador aqui é que "uma classe tem um motivo para mudar". Especialmente ao lidar com estruturas abrangentes, como uma estrutura ORM e / ou UI, seus objetos precisam estar bem isolados, pois as alterações na estrutura geralmente causam alterações nas entidades.

Ao vincular suas entidades às duas estruturas, você está tornando as coisas muito piores.

Telastyn
fonte
7

O objetivo do modelo de exibição é fornecer desacoplamento de duas maneiras: fornecendo dados na forma que o View exige (independente do modelo) e (especialmente no MVVM) empurrando parte ou toda a lógica do View de volta do View para o modelo de exibição.

Em um modelo totalmente normalizado, o Cliente não seria representado no Modelo por uma sequência, mas por um ID. O modelo, se precisar do nome do cliente, procuraria o nome da tabela Customers, usando o CustomerID encontrado na tabela Orders.

Um modelo de exibição, por outro lado, pode estar mais interessado no Namecliente, não no ID. Ele não precisa do ID, a menos que o Cliente esteja sendo atualizado no Modelo.

Além disso, o View Model pode, e geralmente assume, uma forma diferente (a menos que seja puro CRUD ). Um objeto Modelo de exibição de fatura pode ter nome, endereço e itens de linha, mas o modelo contém clientes e descrições.

Em outras palavras, as faturas normalmente não são armazenadas da mesma maneira que são exibidas.

Robert Harvey
fonte
3

De certa forma, você está certo: os dois se referem ao mesmo objeto de domínio, chamado order. Mas você está errado na medida em que não é uma duplicação real do que uma representação diferente - originada de propósitos diferentes. Se você deseja manter seu pedido, por exemplo, deseja acessar todas as colunas. Mas você não quer vazar isso para o exterior. Além de empurrar entidades inteiras através do fio, não é o que você realmente deseja. Conheço casos em que uma coleção de MBs ordersfoi enviada ao cliente, onde alguns kb seriam suficientes.

Para resumir uma longa história - eis as principais razões pelas quais você deseja fazer isso:

1) Encapsulamento - ocultação de informações da sua aplicação

2) Os modelos pequenos são rápidos de serializar e fáceis de transmitir

3) Eles servem a propósitos diferentes, embora estejam sobrepostos, e portanto devem haver objetos diferentes.

4) Às vezes, faz sentido ter para consultas diferentes objetos de valor diferentes . Não sei como é em C #, mas em Java é possível usar um POJO para coletar resultados. Se eu precisar de um único order, uso o Order -Entity, mas se precisar apenas de algumas colunas preenchidas com quantidades de dados, usar Objetos menores será mais eficiente.

Thomas Junk
fonte
Da maneira como a estrutura da entidade é estruturada, as entidades são objetos C # antigos simples, portanto, enviá-los pela rede não é realmente um grande problema. Eu vejo a utilidade de poder enviar apenas parte do conteúdo de um pedido em algumas circunstâncias.
Robert Ricketts
Um ou muitos não é o problema, como eu disse. Mas há casos em que você tem MBs de dados, o que diminui o tempo de carregamento. Pense em tempos de carregamento móveis
Thomas Junk
0

(Isenção de responsabilidade: eu só vi sendo usada dessa maneira. Talvez eu tenha entendido mal o real objetivo de fazê-lo. Trate esta resposta com desconfiança.)

Aqui está outro bit que falta: a conversão entre Ordere OrderModel.

A Orderclasse está vinculada ao seu ORM, enquanto a OrderModelestá vinculada ao design do seu View Model.

Normalmente, os dois métodos serão fornecidos "de uma maneira mágica" (às vezes chamados DI, IoC, MVVM ou outra coisa). Ou seja, em vez de implementar esses dois métodos de conversão como parte dele OrderModel, eles pertencerão a uma classe dedicada à interconversão dessas coisas; essa classe será, de alguma forma, registrada na estrutura, para que ela possa ser facilmente pesquisada.

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

O motivo para fazer isso é que sempre que houver uma passagem do OrderModelpara o Orderou vice-versa, você sabe que alguns dados devem ter sido carregados ou salvos no ORM.

rwong
fonte
Se eu estou mantendo-os ao redor, eu definitivamente gostaria de configurá-los para que a conversão ser mais automatizado
Robert Ricketts
@RobertRicketts A coisa que eu vi fica assim: IMvxValueConverter . Embora eu possa ter entendido mal o seu propósito.
rwong