c# - tripodizacion - via de insercion protesis fija
Patrón de diseño para clase con más de 100 propiedades. (17)
¿Siempre necesitas todas las propiedades que se devuelven? ¿Puede usar la proyección con cualquier clase que esté consumiendo los datos y solo generar las propiedades que necesita en ese momento?
¿Qué consejos / sugerencias / consejos darías para diseñar una clase que tenga más de 100 propiedades?
Fondo
- La clase describe una factura. Una factura puede tener más de 100 atributos que la describen, es decir, fecha, monto, código, etc.
- El sistema al que enviamos la factura utiliza cada uno de los 100 atributos y se envía como una entidad única (a diferencia de varias partes que se envían en diferentes momentos).
- Los atributos que describen la factura se requieren como parte del proceso de negocio. El proceso de negocio no puede ser cambiado.
Sugerencias?
- ¿Qué han hecho otros cuando se enfrentan al diseño de una clase que tiene 100 atributos? ¿Es decir, crear la clase con cada una de las 100 propiedades?
- De alguna manera romperlo (si es así, ¿cómo)?
- ¿O es esto una ocurrencia bastante normal en tu experiencia?
EDITAR Después de leer algunas respuestas geniales y pensar más en esto, no creo que realmente haya una respuesta única para esta pregunta. Sin embargo, desde que terminamos modelando nuestro diseño siguiendo las líneas de la Respuesta de LBrushkin, le he dado crédito. Si bien no es la respuesta más popular, la respuesta de LBrushkin nos ayudó a definir varias interfaces que agregamos y reutilizamos a lo largo de la aplicación, y nos empujó a investigar algunos patrones que pueden ser útiles en el futuro.
A menos que su código realmente use muchos de los atributos en muchos lugares, me gustaría ir a buscar un diccionario.
Tener propiedades reales tiene sus ventajas (seguridad de tipo, capacidad de detección / inteligencia, refactorabilidad), pero no importa si todo el código lo hace, se obtiene de otra parte, se muestra en la interfaz de usuario, se envía en un servicio web, se guarda en un archivo, etc. .
Diccionario? por qué no, pero no necesariamente. Veo una etiqueta de C #, tu lenguaje tiene reflejo, bueno para ti. Tuve algunas clases demasiado grandes como esta en mi código Python, y la reflexión ayuda mucho:
for attName in ''attr1'', ''attr2'', ..... (10 other attributes):
setattr( self, attName, process_attribute( getattr( self, attName ))
Cuando desee convertir 10 miembros de cadena de alguna codificación a UNICODE, no se deben tocar otros miembros de cadena, se debe aplicar un poco de procesamiento numérico a otros miembros ... tipos de conversión ... a para ritmos de bucle copiar y pegar lotes de código en cualquier momento para la limpieza.
El diseño defectuoso está obviamente en el sistema al que se está enviando; ninguna factura tiene más de 100 propiedades que no se pueden agrupar en una subestructura. Por ejemplo, una factura tendrá un cliente y un cliente tendrá una identificación y una dirección. La dirección a su vez tendrá una calle, un código postal y qué más. Pero todas estas propiedades no deben pertenecer directamente a la factura: una factura no tiene identificación de cliente ni código postal.
Si tiene que crear una clase de factura con todas estas propiedades directamente asociadas a la factura, le sugiero que haga un diseño limpio con varias clases para un cliente, una dirección y todas las demás cosas necesarias y luego simplemente ajuste este gráfico de objetos bien diseñado con una clase de factura grande que no tiene almacenamiento ni lógica, simplemente pasa todas las operaciones al gráfico de objetos que se encuentra detrás.
Hmmm ... ¿Todos estos son realmente relevantes específicamente , y solo para la factura? Típicamente lo que he visto es algo como:
class Customer:
.ID
.Name
class Address
.ID
.Street1
.Street2
.City
.State
.Zip
class CustomerAddress
.CustomerID
.AddressID
.AddressDescription ("ship","bill",etc)
class Order
.ID
.CustomerID
.DatePlaced
.DateShipped
.SubTotal
class OrderDetails
.OrderID
.ItemID
.ItemName
.ItemDescription
.Quantity
.UnitPrice
Y atándolo todo junto:
class Invoice
.OrderID
.CustomerID
.DateInvoiced
Cuando imprima la factura, junte todos estos registros.
Si realmente debe tener una sola clase con más de 100 propiedades, puede ser mejor usar un diccionario
Dictionary<string,object> d = new Dictionary<string,object>();
d.Add("CustomerName","Bob");
d.Add("ShipAddress","1600 Pennsylvania Ave, Suite 0, Washington, DC 00001");
d.Add("ShipDate",DateTime.Now);
....
La idea aquí es dividir tu en unidades lógicas. En el ejemplo anterior, cada clase corresponde a una tabla en una base de datos. Puede cargar cada uno de estos en una clase dedicada en su capa de acceso a datos, o seleccionar con una combinación de las tablas donde se almacenan al generar su informe (factura).
Me imagino que algunas de estas propiedades probablemente estén relacionadas entre sí. Me imagino que probablemente hay grupos de propiedades que definen facetas independientes de una Factura que tienen sentido como grupo.
Es posible que desee considerar la creación de interfaces individuales que modelen las diferentes facetas de una factura. Esto puede ayudar a definir los métodos y propiedades que operan en estas facetas de una manera más coherente y fácil de entender.
También puede elegir combinar propiedades que tengan un significado particular (direcciones, ubicaciones, rangos, etc.) en objetos que agregue, en lugar de propiedades individuales de una sola clase grande.
Tenga en cuenta que la abstracción que elija para modelar un problema y la abstracción que necesita para comunicarse con algún otro sistema (o proceso empresarial) no tiene que ser la misma. De hecho, a menudo es productivo aplicar el patrón de puente para permitir que las abstracciones separadas evolucionen de manera independiente.
No debes estar motivado puramente por consideraciones estéticas.
Según sus comentarios, el objeto es básicamente un objeto de transferencia de datos consumido por un sistema heredado que espera la presencia de todos los campos.
A menos que haya un valor real en la composición de este objeto a partir de partes, ¿qué se gana precisamente al ocultar su función y propósito?
Estas serían razones válidas:
1 - Está recopilando la información para este objeto de varios sistemas y las partes son relativamente independientes. Tendría sentido componer el objeto final en ese caso basado en consideraciones de proceso.
2 - Tiene otros sistemas que pueden consumir varios subconjuntos de los campos de este objeto. Aquí la reutilización es el factor motivador.
3 - Existe una posibilidad muy real de un sistema de facturación de próxima generación basado en un diseño más racional. Aquí la extensibilidad y la evolución del sistema son el factor motivador.
Si ninguna de estas consideraciones es aplicable en su caso, ¿cuál es el punto?
Podría intentar ''normalizarlo'' como si fuera una tabla de base de datos. Tal vez ponga todas las propiedades relacionadas con la dirección en una clase de Address
, por ejemplo, tenga una propiedad BillingAddress
y MailingAddress
de tipo Address
en su clase de Invoice
. Estas clases podrían ser reutilizadas más tarde también.
Podrías probar LINQ, se auto-generará propiedades para ti. Si todos los campos están repartidos en varias tablas, podría crear una vista y arrastrarla a su diseñador.
Puede encontrar algunas ideas útiles en este artículo sobre las propiedades de Steve Yegge, llamadas el Patrón de diseño universal .
Se considera un estilo de OO malo, pero si todo lo que está haciendo es rellenar un objeto con propiedades para pasarlas hacia adelante para su procesamiento, y el procesamiento solo lee las propiedades (probablemente para crear otro objeto o actualizaciones de base de datos), tal vez un POD simple objeto es lo que necesita, tener todos los miembros públicos, un constructor predeterminado y ningún otro método miembro. Por lo tanto, se puede tratar como un contenedor de propiedades en lugar de un objeto en toda regla.
Serían demasiadas columnas cuando su clase / tabla en la que la almacene comience a violar las reglas de normalización.
En mi experiencia, ha sido muy difícil obtener tantas columnas cuando se está normalizando correctamente. Aplique las reglas de normalización a la tabla / clase amplia y creo que terminará con menos columnas por entidad.
Si lo que está intentando crear es una puerta de enlace de tabla para la tabla de 100 columnas preexistente para este otro servicio, una lista o diccionario puede ser una forma bastante rápida de comenzar. Sin embargo, si está recibiendo información de un formulario grande o asistente de UI, probablemente tendrá que validar el contenido antes de enviarlo a su servicio remoto.
Un DTO simple podría tener este aspecto:
class Form
{
public $stuff = array();
function add( $key, $value ) {}
}
Una puerta de enlace de tabla podría ser más como:
class Form
{
function findBySubmitId( $id ) {} // look up my form
function saveRecord() {} // save it for my session
function toBillingInvoice() {} // export it when done
}
Y podría extender eso con bastante facilidad dependiendo de si tiene variaciones de la factura. (Agregar un método validate () para cada subclase podría ser apropiado).
class TPSReport extends Form {
function validate() {}
}
Si desea separar su DTO del mecanismo de entrega, ya que el mecanismo de entrega es genérico para todas sus facturas, eso podría ser fácil. Sin embargo, es posible que se encuentre en una situación en la que haya lógica empresarial relacionada con el éxito o el fracaso de la factura. Y aquí es donde me estoy yendo a la maleza. Pero es donde y el modelo de OO puede ser útil ... pagaré un centavo por las diferentes facturas y los diferentes procedimientos para las diferentes facturas, y si los envíos de facturas no requieren rutinas adicionales :-)
class Form {
function submitToBilling() {}
function reportFailedSubmit() {}
function reportSuccessfulSubmit() {}
}
class TPSReport extends Form {
function validate() {}
function reportFailedSubmit() { /* oh this goes to AR */ }
}
Note la respuesta de David Lively: es una buena idea. A menudo, los campos en un formulario son cada uno sus propias estructuras de datos y tienen sus propias reglas de validación. Así que puedes modelar objetos compuestos bastante rápido. Esto asociaría cada tipo de campo con sus propias reglas de validación y forzaría una tipificación más estricta.
Si tiene que profundizar en la validación, a menudo las reglas comerciales son un modelo completamente diferente de los formularios o DTO que los suministran. También podría enfrentarse a la lógica que está orientada por departamento y tiene poco que ver con la forma. Es importante mantener eso fuera de la validación del formulario en sí y del proceso de envío de modelos por separado.
Si está organizando un esquema detrás de estos formularios, en lugar de una tabla con 100 columnas, probablemente dividiría las entradas por identificadores de campo y valores, en solo unas pocas columnas.
table FormSubmissions (
id int
formVer int -- fk of FormVersions
formNum int -- group by form submission
fieldName int -- fk of FormFields
fieldValue text
)
table FormFields (
id int
fieldName char
)
table FormVersions (
id
name
)
select s.* f.fieldName from FormSubmissions s
left join FormFields f on s.fieldName = f.id
where formNum = 12345 ;
Yo diría que este es definitivamente un caso en el que querrá volver a tener en cuenta su camino hasta encontrar algo cómodo. Esperemos que tenga algún control sobre cosas como el esquema y su modelo de objeto. (Por cierto ... ¿esa tabla se conoce como "normalizada"? He visto variaciones en ese esquema, típicamente organizado por tipo de datos ... ¿bueno?)
Si una entidad tiene cien atributos únicos que una sola clase con cien propiedades, es lo correcto.
Puede ser posible dividir cosas como direcciones en una subclase, pero esto se debe a que una dirección es realmente una entidad en sí misma y se reconoce fácilmente como tal.
Una factura de libro de texto (es decir, simplificada en exceso no utilizable en el mundo real) se vería así:
class invoice:
int id;
address shipto_address;
address billing_address;
order_date date;
ship_date date;
.
.
.
line_item invoice_line[999];
class line_item;
int item_no.
int product_id;
amt unit_price;
int qty;
amt item_cost;
.
.
.
Así que estoy sorprendido de que no tenga al menos una serie de elementos de línea allí.
¡Acostumbrarse a él! En el mundo de los negocios, una entidad puede tener fácilmente cientos y, a veces, miles de atributos únicos.
Suena como para el resultado final que necesita para producir un objeto de factura con alrededor de 100 propiedades. ¿Tienes 100 tales propiedades en cada caso? Quizás desee una fábrica, una clase que produzca una factura dado un conjunto más pequeño de parámetros. Se podría agregar un método de fábrica diferente para cada escenario donde los campos relevantes de la factura sean relevantes.
Utilicé un Diccionario <cadena, cadena> para algo como esto.
viene con un montón de funciones que pueden procesarlo, es fácil convertir cadenas a otras estructuras, fácil de almacenar, etc.
si todo lo demás falla, al menos divida la clase en varias clases parciales para tener una mejor legibilidad. También facilitará que el equipo trabaje en paralelo en diferentes partes de esta clase.
buena suerte :)