¿Patrón de duplicación de clase?

11

Actualmente estoy trabajando como desarrollador en solitario en mi proyecto actual. Heredé el proyecto de otro desarrollador, que desde entonces dejó la empresa. Es una aplicación web de estilo modelo-vista-controlador en C #. Utiliza Entity Framework para el mapeo relacional de objetos. Y hay dos conjuntos diferentes de clases para los tipos en el modelo de dominio. Un conjunto se usa para interactuar con el ORM y el otro se usa como modelo en el sistema MVC. Por ejemplo, puede haber dos clases de la siguiente manera:

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

y

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
    }

}

Puedo pensar en varios inconvenientes de este enfoque (más lugares para cambiar el código si algo cambia, más objetos sentados en la memoria, más tiempo dedicado a copiar datos), pero no estoy realmente seguro de cuáles son las ventajas aquí. ¿Más flexibilidad para las clases de modelos, supongo? Pero podría obtener eso subclasificando las clases de entidad también. Mi inclinación sería fusionar estos dos grupos de clases, o posiblemente hacer que las clases modelo sean subclases de las clases de entidad. Entonces, ¿me estoy perdiendo algo importante aquí? ¿Es este un patrón de diseño común que no conozco? ¿Hay buenas razones para no seguir con el refactor que estoy contemplando?

ACTUALIZAR

Algunas de las respuestas aquí me hacen darme cuenta de que mi descripción inicial del proyecto carecía de algunos detalles importantes. También hay un tercer grupo de clases que existen en el proyecto: las clases de modelo de página. Son los que realmente se utilizan como modelo que respalda la página. También contienen información específica de la interfaz de usuario y no se almacenarían con un pedido en la base de datos. Una clase de modelo de página de ejemplo podría ser:

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

Veo totalmente que la utilidad de este tercer grupo es distinta aquí, y no tengo planes de fusionarlo con otra cosa (aunque podría cambiarle el nombre).

Las clases en el grupo de modelos también son utilizadas actualmente por la API de la aplicación, que también me interesaría escuchar comentarios sobre si es una buena idea.

También debo mencionar que el cliente que se representa como una cadena aquí fue para simplificar el ejemplo, no porque en realidad se esté representando de esa manera en el sistema. El sistema real tiene al cliente como un tipo distinto en el modelo de dominio, con sus propias propiedades.

Robert Ricketts
fuente
1
"Heredé el proyecto de otro desarrollador, que desde entonces dejó la compañía" hay un sitio dedicado a historias que comienzan de esta manera .
Con respecto a su actualización: eso parece demasiada indirección para un beneficio insuficiente.
Robert Harvey
@RobertHarvey ¿A qué te refieres con ser demasiado indirecto?
Robert Ricketts
1
El modelo de orden. OrderViewModel (a lo que probablemente se refiere como EditOrderPageModel) es suficiente indirección; mira mi respuesta a continuación.
Robert Harvey
@RobertHarvey Esto suena como la respuesta a la pregunta que realmente estaba tratando de hacer
Robert Ricketts

Respuestas:

16

Entonces, ¿me estoy perdiendo algo importante aquí?

SI

Si bien estos se parecen a la misma cosa y representan la misma cosa en el dominio, no son los mismos objetos (OOP).

Uno es el Orderconocido por la parte de almacenamiento de datos del código. El otro es el Orderconocido por la interfaz de usuario. Si bien es una coincidencia agradable que estas clases tengan las mismas propiedades, no están garantizadas .

O para verlo desde otra perspectiva, considere el Principio de responsabilidad única. El motivador clave aquí es que "una clase tiene una razón para cambiar". Especialmente cuando se trata de marcos omnipresentes como un marco ORM y / o UI, sus objetos deben estar bien aislados, ya que los cambios en el marco a menudo harán que sus entidades cambien.

Al vincular sus entidades a ambos marcos, lo está haciendo mucho peor.

Telastyn
fuente
7

El propósito del modelo de vista es proporcionar desacoplamiento de dos maneras: proporcionando datos en la forma que requiere la vista (independiente del modelo) y (especialmente en MVVM) empujando parte o la totalidad de la lógica de vista de la vista al modelo de vista.

En un modelo totalmente normalizado, el Cliente no estaría representado en el Modelo por una cadena, sino por una ID. El Modelo, si necesita el nombre del cliente, buscaría el nombre de la tabla Clientes, utilizando el CustomerID que se encuentra en la tabla Pedidos.

Un modelo de vista, por otro lado, puede estar más interesado en el Namedel cliente, no en el ID. No necesita la ID a menos que el Cliente se actualice en el Modelo.

Además, el modelo de vista puede, y generalmente lo hace, tomar una forma diferente (a menos que sea CRUD puro ). Un objeto Modelo de vista de factura puede tener nombre de cliente, direcciones y líneas de pedido, pero el modelo contiene clientes y descripciones.

En otras palabras, las facturas normalmente no se almacenan de la misma manera que se muestran.

Robert Harvey
fuente
3

En cierto sentido tiene razón: ambos se refieren al mismo domainobject, que se llama order. Pero está equivocado en la medida en que no es una duplicación real que una representación diferente, originada con diferentes propósitos. Si desea conservar su pedido, por ejemplo, desea acceder a todas y cada una de las columnas. Pero no quieres filtrar eso al exterior. Además, empujar entidades enteras a través del cable no es lo que realmente quieres. Sé de casos en los que ordersse envió una colección de MBs al cliente donde unos pocos kb serían suficientes.

Para resumir, estas son las razones principales por las que desea hacer eso:

1) Encapsulación: ocultación de información de su aplicación

2) Los modelos pequeños son rápidos de serializar y fáciles de transmitir

3) Sirven para diferentes propósitos, aunque se superponen y, por lo tanto, debe haber diferentes objetos.

4) A veces tiene sentido tener para diferentes consultas diferentes objetos de valor . No sé cómo es en C #, pero en Java es posible usar un POJO para recopilar resultados. Si necesito una sola order, uso Order -Entity, pero si solo necesito algunas columnas llenas de cantidades en los datos, usar objetos más pequeños es más eficiente.

Thomas Junk
fuente
La forma en que se estructura el marco de la entidad, las entidades son simples objetos C # antiguos, por lo que enviarlos por cable no es realmente un gran problema. Veo la utilidad de poder enviar solo una parte del contenido de un pedido en algunas circunstancias.
Robert Ricketts
Uno o un grupo no es el problema como dije. Pero hay casos en los que tiene MB de datos, lo que ralentiza su tiempo de carga. Piense en los tiempos de carga móvil
Thomas Junk,
0

(Descargo de responsabilidad: solo vi que se usaba de esta manera. Podría haber entendido mal el verdadero propósito de hacerlo. Trate esta respuesta con sospecha).

Aquí hay otro bit que falta: la conversión entre Ordery OrderModel.

La Orderclase está vinculada a su ORM, mientras que OrderModelestá vinculada al diseño de su Modelo de vista.

Por lo general, los dos métodos se proporcionarán "de forma mágica" (a veces llamados DI, IoC, MVVM, o incluso otra cosa). Es decir, en lugar de implementar estos dos métodos de conversión como parte de OrderModel, pertenecerán a una clase dedicada a la conversión de estas cosas; esa clase se registrará de alguna manera con el marco para que el marco pueda buscarlo fácilmente.

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

La razón para hacer esto es que cada vez que hay un cruce desde el OrderModelhacia Ordero viceversa, usted sabe que algunos datos deben haberse cargado o guardado en ORM.

rwong
fuente
Si me quedo con ambos, definitivamente me gustaría configurarlos para que la conversión sea más automatizada
Robert Ricketts
@RobertRicketts Lo que vi se ve así: IMvxValueConverter . Aunque puedo haber entendido mal su propósito.
rwong