Pregunta

¿Qué consejos/sugerencias/directrices que usted proporciona para el diseño de una clase que tiene más de 100 propiedades?

De fondo

  • La clase se describe una factura.Una factura puede tener más de 100 atributos de describir, es decir,fecha, cantidad, código, etc...
  • El sistema que estamos presentación de la factura a los usos de cada uno de los 100 atributos y es presentado como una sola entidad (en contraposición a las distintas partes de ser presentado en diferentes momentos).
  • Los atributos que describen la factura son requeridos como parte del proceso de negocio.El proceso de negocio no puede ser cambiado.

Sugerencias?

  • Lo que otros han hecho cuando se enfrentan con el diseño de una clase que tiene más de 100 atributos?es decir, crear la clase con cada uno de los 100 propiedades?
  • De alguna manera romper (si es así, cómo)?
  • O esto es algo bastante normal en su experiencia?

EDITAR Después de leer a través de algunos grandes respuestas y pensar más en ello, no creo que realmente hay una sola respuesta para esta pregunta.Sin embargo, desde que terminó el modelado de nuestro diseño a lo largo de las líneas de LBrushkin la Respuesta Yo le he dado crédito.Aunque no sea la respuesta más popular, LBrushkin la respuesta de la ayudó a llevarnos a la definición de varias interfaces que hemos agregado y la reutilización de toda la aplicación, así como le dio un codazo a nosotros en la investigación de algunos de los patrones que pueden ser útiles en el camino.

¿Fue útil?

Solución

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 como 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 de negocio) 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 forma independiente.

Otros consejos

Usted podría tratar de "normalizar" es como si fuera una tabla de base de datos.Tal vez poner todas las direcciones relacionadas con las propiedades en una Address la clase, por ejemplo - a continuación, tienen un BillingAddress y MailingAddress la propiedad de tipo Address en su Invoice clase.Estas clases podrían ser reutilizados más tarde también.

El diseño incorrecto está obviamente en el sistema al que está enviando: ninguna factura tiene más de 100 propiedades que no pueden agruparse 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 deberían 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 adjuntas 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 requeridas y luego simplemente envuelva bien gráfico de objeto diseñado con una clase de factura gruesa que no tiene almacenamiento y lógica en sí misma, simplemente pasando todas las operaciones al gráfico de objeto detrás.

Hmmm ... ¿Son todos esos realmente relevantes específicamente , y solo para la factura? Por lo general, 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 uniéndolo todo:

class Invoice
.OrderID
.CustomerID
.DateInvoiced

Al imprimir la factura, combine 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 su 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).

A menos que su código realmente use muchos de los atributos en muchos lugares, en su lugar, buscaría un diccionario.

Tener propiedades reales tiene sus ventajas (seguridad de tipo, capacidad de detección / inteligencia, refactorabilidad), pero esto no importa si todo el código lo hace es obtenerlo de otra parte, mostrarlo en la interfaz de usuario, enviar un servicio web, guardar en un archivo, etc.

Sería demasiadas columnas cuando su clase / tabla en la que la almacena comienza 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.

Se considera un mal estilo OO, pero si todo lo que está haciendo es poblar un objeto con propiedades para pasarlos al procesamiento, y el procesamiento solo lee las propiedades (presumiblemente para crear algún otro objeto o actualizaciones de la base de datos), tal vez lo que necesita es un simple objeto POD, que tenga todos los miembros públicos, un constructor predeterminado y ningún otro método miembro. Por lo tanto, puede tratarlo como un contenedor de propiedades en lugar de un objeto completo.

Usé un Diccionario < cadena, cadena > por 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.

Puede encontrar algunas ideas útiles en este artículo sobre las propiedades de Steve Yegge llamado Patrón de diseño universal .

No debe estar motivado únicamente 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 componer 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 en función de las consideraciones del 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?

Parece que para el resultado final necesita producir un objeto de factura con alrededor de 100 propiedades. ¿Tiene 100 propiedades de este tipo en todos los casos? Tal vez 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 son relevantes.

Si lo que está intentando crear es una pasarela de tabla para pre tabla existente de 100 columnas para este otro servicio, una lista o diccionario podría ser una forma bastante rápida de comenzar. Sin embargo, si está recibiendo información de un formulario grande o un asistente de IU, probablemente tendrá que validar el contenido antes de enviarlo a su servicio remoto.

Un DTO simple podría verse así:

class Form
{
    public $stuff = array();
    function add( $key, $value ) {}
}

Una pasarela 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 extenderlo fácilmente 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, porque 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 exista una lógica comercial en torno al éxito o el fracaso de la factura. Y aquí es donde me voy a las malezas. Pero es donde y el modelo OO puede ser útil ... Apostaré un centavo para que haya diferentes facturas y diferentes procedimientos para diferentes facturas, y si las facturas de envío de facturas, necesitará rutinas adicionales :-)

class Form { 
    function submitToBilling() {}
    function reportFailedSubmit() {}
    function reportSuccessfulSubmit() {}
}
class TPSReport extends Form { 
    function validate() {}
    function reportFailedSubmit() { /* oh this goes to AR */ }
}

Nota la respuesta de David Livelys: 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. Por lo tanto, puede modelar objetos compuestos con bastante rapidez. Esto asociaría cada tipo de campo con sus propias reglas de validación y exigiría una escritura más estricta.

Si tiene que avanzar en la validación, a menudo las reglas de negocio son un modelo completamente diferente de los formularios o los DTO que los suministran. También podría enfrentarse a una lógica orientada por departamento y que tiene poco que ver con el formulario. Es importante mantener eso fuera de la validación del formulario en sí y del proceso de envío del modelo por separado.

Si está organizando un esquema detrás de estos formularios, en lugar de una tabla con 100 columnas, probablemente desglosarí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 ;

Diría que este es definitivamente un caso en el que querrás re-factorizar tu camino hasta que encuentres algo cómodo. Esperemos que tenga algo de control sobre cosas como el esquema y su modelo de objetos. (Por cierto ... ¿se conoce esa tabla como 'normalizada'? He visto variaciones en ese esquema, generalmente organizadas por tipo de datos ... ¿bueno?)

¿Siempre necesita 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?

Podrías probar LINQ, automáticamente generará propiedades por ti. Si todos los campos están distribuidos en varias tablas y puede crear una vista y arrastrar la vista a su diseñador.

Diccionario? por qué no, pero no necesariamente. Veo una etiqueta C #, su lenguaje tiene reflejo, bien por usted. Tuve algunas clases demasiado grandes como esta en mi código de 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, algunos otros miembros de cadena no deben tocarse, desea aplicar un procesamiento numérico a otros miembros ... tipos de conversión ... a para copia de tiempos de bucle -pasting mucho código en cualquier momento para la limpieza.

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 fácilmente reconocible 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 me sorprende que no tenga al menos una serie de line_items allí.

¡Acostúmbrate! En el mundo de los negocios, una entidad puede tener fácilmente cientos y, a veces, miles de atributos únicos.

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 :)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top