Evitar constructores con muchos argumentos.

10

Entonces tengo una fábrica que crea objetos de diferentes clases. Las posibles clases se derivan de un antepasado abstracto. La fábrica tiene un archivo de configuración (sintaxis JSON) y decide qué clase crear, según la configuración del usuario.

Para lograr esto, la fábrica utiliza boost :: property_tree para el análisis JSON. Camina por la calle y decide qué objeto concreto crear.

Sin embargo, los objetos del producto tienen muchos campos (atributos). Dependiendo de la clase concreta, el objeto tiene unos 5-10 atributos, en el futuro tal vez incluso más.

Así que no estoy seguro de cómo debería ser el constructor de los objetos. Se me ocurren dos soluciones:

1) El constructor del producto espera cada atributo como parámetro, por lo tanto, el constructor terminará con más de 10 parámetros. Esto será feo y dará lugar a líneas de código largas e ilegibles. Sin embargo, la ventaja es que la fábrica puede analizar el JSON e invocar al constructor con los parámetros correctos. La clase de producto no necesita saber que se ha creado debido a la configuración de JSON. No necesita saber que hay JSON o configuración involucrada en absoluto.

2) El constructor del producto solo espera un argumento, el objeto property_tree. Entonces puede analizar la información necesaria. Si falta información de la configuración o está fuera de los límites, cada clase de producto puede reaccionar correctamente. La fábrica no necesita saber qué argumentos necesitan los diversos productos. La fábrica tampoco necesita saber cómo reaccionar en caso de una configuración incorrecta. Y la interfaz del constructor es unificada y pequeña. Pero, como desventaja, el producto necesita extraer la información necesaria del JSON, por lo tanto, sabe cómo se construye.

Tiendo a preferir la solución 2). Sin embargo, no estoy seguro de si este es un buen patrón de fábrica. De alguna manera se siente mal dejar que el producto sepa que está creado con la configuración JSON. Por otro lado, se pueden introducir nuevos productos de forma muy sencilla.

¿Alguna opinión sobre eso?

lugge86
fuente
1
Seguí tu enlace. Hay un ejemplo en la respuesta mejor calificada de Ratchet Freak. Pero, ¿qué problema resuelve este enfoque de "constructor"? Ahí está la línea de código DataClass data = builder.createResult () ;. Pero el método createResults () todavía tiene que obtener los 10 parámetros en el objeto DataClass. ¿Pero cómo? Parece que solo tienes una capa más de abstracción, pero el constructor de DataClass no se vuelve más pequeño.
lugge86
Echa un vistazo a constructor y prototipo.
Silviu Burcea
Silviu Burcea, lo hice. Sin embargo, cuando se usa el constructor, ¿cómo obtiene el constructor los parámetros en el producto? En algún lugar tiene que haber una interfaz gorda. El constructor es solo una capa más, pero de alguna manera los parámetros tienen que encontrar su camino en la clase de producto.
lugge86
1
Si su clase es demasiado grande, cambiar los argumentos del constructor no hará que no sea demasiado grande .
Telastyn

Respuestas:

10

No haría la opción 2, porque entonces has enredado para siempre la construcción de tu objeto con el análisis del árbol de propiedades de impulso. Si se siente cómodo con una clase que necesita tantos parámetros, debería sentirse cómodo con un constructor que necesita tantos parámetros, ¡así es la vida!

Si su principal preocupación es la legibilidad del código, puede usar el patrón de construcción, es básicamente el c ++ / java stopgap por falta de argumentos con nombre. Terminas con cosas que se ven así:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

Así que ahora MyObject tendrá un constructor privado, que se llama en Builder :: build. Lo bueno es que ese será el único lugar donde tendrá que llamar a un constructor con 10 parámetros. La fábrica de árbol de propiedades de impulso usará el constructor y, posteriormente, si desea construir un MyObject directamente o desde una fuente diferente, pasará por el constructor. Y el generador básicamente le permite nombrar claramente cada parámetro a medida que lo pasa, por lo que es más legible. Obviamente, esto agrega algo de repetitivo, por lo que tendrá que decidir si vale la pena en comparación con solo llamar al constructor desordenado o agrupar algunos de sus parámetros existentes en estructuras, etc. Simplemente arrojando otra opción sobre la mesa.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example

Nir Friedman
fuente
5

NO use el segundo enfoque.

Definitivamente no es la solución y solo conduciría a crear instancias de clases en la lógica de su negocio, en lugar de la parte de su aplicación donde están las fábricas.

Ya sea:

  • intenta agrupar ciertos parámetros que parecen representar cosas similares en objetos
  • dividir la clase actual en varias clases más pequeñas (tener una clase de servicio con 10 parámetros parece que la clase hace demasiadas cosas)
  • déjelo como está, si su clase no es realmente un servicio, sino un objeto de valor

A menos que el objeto que está creando sea en realidad una clase responsable de mantener los datos, debe intentar refactorizar el código y dividir la clase grande en otras más pequeñas.

Andy
fuente
Bueno, la lógica de negocios está utilizando una fábrica que devuelve los productos, por lo tanto, la lógica de negocios no ve el material JSON / ptree. Pero veo su punto, tener el código Parser en el constructor se siente mal.
lugge86
La clase representa un widget en una GUI para un sistema incrustado, por lo tanto, 5+ atributos me parecen bien: x_coord, y_coord, backgroundcolor, framesize, framecolor, text ...
lugge86
1
@ lugge86 Aunque esté utilizando una fábrica para analizar el JSON y, por lo tanto, evite llamar newo construir objetos dentro de su lógica comercial, no es un diseño muy bueno. Marque la charla No buscar cosas de Miško Hevery , quien explica con más profundidad por qué el enfoque de fábrica que usted insinuó es malo tanto desde el punto de vista de la prueba como de la lectura. Además, su clase parece ser un objeto de datos, y para aquellos generalmente está bien tener más parámetros que la clase de servicio regular. No me molestaría demasiado.
Andy
Me siento bien con mi enfoque de fábrica, pero seguiré tu enlace y pensaré en ello. Sin embargo, la fábrica no está en cuestión en este tema. La pregunta sigue siendo cómo configurar los productos ...
lugge86
"Tener una clase de servicio con 10 parámetros parece que la clase hace demasiadas cosas" No en el aprendizaje automático. Cualquier algoritmo de ML tendría toneladas de parámetros ajustables. Me pregunto cuál es la forma correcta de lidiar con eso al codificar ML.
Siyuan Ren
0

La opción 2 es casi correcta.

Una opción mejorada 2

Cree una clase "frontal" cuyo trabajo es tomar ese objeto de estructura JSON y seleccionar los bits y llamar a los constructores de fábrica. Toma lo que hace la fábrica y se lo da al cliente.

  • La fábrica no tiene absolutamente ninguna idea de que tal cosa JSON exista.
  • El cliente no tiene que saber qué bits específicos necesita la fábrica.

Básicamente, el "front-end" está diciendo a los 2 Bobs: "¡Trato con los clientes redactados para que los ingenieros no tengan que hacerlo! ¡Tengo habilidades con la gente!" Pobre Tom. Si solo hubiera dicho "desacople al cliente de la construcción. Este resultado es una fábrica altamente cohesionada"; él podría haber mantenido su trabajo.

¿Demasiados argumentos?

No es para el cliente: comunicación frontal.

Front end - fábrica? Si no hay 10 parámetros, lo mejor que puede hacer es posponer el desempaquetado, si no es lo JSON original, algo de DTO. ¿Es esto mejor que pasar el JSON a la fábrica? La misma diferencia que digo.

Consideraría fuertemente pasar parámetros individuales. Apéguese al objetivo de una fábrica limpia y cohesiva. Evite las preocupaciones de la respuesta de @DavidPacker.

Mitigar "demasiados argumentos"

  • Constructores de fábrica o clase

    • tomando solo argumentos para la construcción específica de clase / objeto.
    • parámetros predeterminados
    • parámetros opcionales
    • argumentos nombrados
  • Agrupación de argumentos frontales

    • Examina, evalúa, valida, establece, etc. valores de argumentos guiados por las firmas del constructor anteriores.
radarbob
fuente
"La fábrica no tiene absolutamente ninguna idea de que tal cosa JSON existe" - bueno, entonces, ¿para qué es la fábrica? Oculta los detalles de la creación del producto tanto del producto como del consumidor. ¿Por qué debería ayudar otra clase? Estoy bien con la fábrica hablando JSON. Uno puede implementar otra fábrica para analizar XML e implementar "Abstract Factory" en el futuro ...
lugge86
Sr. Factory: "¿Qué objeto quieres? ... ¿Qué es eso? Solo dime qué objeto de clase construir". El archivo de configuración JSON es una fuente de datos, como dice el tío Bob "es un detalle de implementación". Podría de otra fuente y / o de otra forma. En general, queremos desacoplar los detalles específicos del origen de datos. Si la fuente o la forma cambian, la fábrica no lo hará. Dado un fuente + analizador, y una fábrica como módulos desacoplados hace que ambos sean reutilizables.
radarbob