2011-08-31 13 views
7

Tenemos algunos objetos en nuestro modelo de dominio con lo que se Cómicamente término ofensivamente grandes constructores, tan grande que IntelliSense se da por vencido tratando de mostrar todo a usted ...refactoración grandes constructores

Cue un tipo con 50 o más argumentos, la mayoría de los tipos de valor, algunos tipos de referencia:

public class MyLegacyType 
{ 
    public MyLegacyType(int a1, int a2, int a3, ... int a50) // etc 
    { 
    } 
} 

lo voy a decir ahora, sin este tipo no se puede cambiar. El tipo en sí mismo representa lógicamente una entidad, que resulta ser muy pesada. Los usuarios que crean este tipo proporcionan la mayoría de los argumentos de múltiples fuentes, aunque algunos están predeterminados. Quizás haya un patrón para que las fuentes se proporcionen a la construcción en lugar de los resultados.

Sin embargo, lo que puede cambiar es cómo se crea el tipo. Actualmente tenemos secciones de código que sufren de:

Una respuesta inmediata es utilizar parámetros opcionales para valores predeterminados y argumentos con nombre para ayudar con la fusión. Hacemos esto hasta cierto punto en otros tipos, funciona bien.

Sin embargo, se siente como si esto estuviera a mitad de camino de la refactorización completa.

La otra solución obvia es reducir los parámetros de constructor con tipos de contenedor que tienen propiedades para lo que solían ser argumentos de constructor. Esto ordena los constructores muy bien, y le permite incrustar valores predeterminados en los contenedores, pero esencialmente mueve el problema a otro tipo y posiblemente equivale al uso de parámetros opcionales/nombrados.

También existe el concepto de constructores Fluent ... tanto en una propiedad (WithIntA, WithIntB) como en un tipo de contenedor (WithTheseInts(IntContainer c)). Personalmente, me gusta este enfoque desde el punto de vista de las llamadas, pero de nuevo en un tipo grande se vuelve prolijo y se siente como si acabara de mover un problema en lugar de resolverlo.

Mi pregunta, si hay una enterrada en este lío, es: ¿son estas tácticas de refactorización viables para el problema? Por favor, responda un poco con alguna experiencia relevante, trampas o críticas. Me estoy inclinando por las cosas Fluent, porque creo que se ve bien y es bastante legible y fácil de combinar.

Siento que me falta el Santo Grial de las refactorizaciones de constructores, así que estoy abierto a sugerencias. Por supuesto, esto también podría ser un efecto secundario desafortunado e inevitable de tener un tipo con muchas propiedades en primer lugar ...

+1

Me gustaría ir con su segundo enfoque (tipos de contenedor que tienen propiedades para lo que solían ser argumentos de constructor) para argumentos de constructor opcionales. Las interfaces fluidas son agradables, pero pueden ser un poco complicadas si tiene que encadenar muchas llamadas de método. IMO –

Respuesta

12

Obviamente no tenemos mucho contexto aquí, pero con más de 50 parámetros mi interpretación es que esta clase está haciendo demasiado y es demasiado compleja. Comenzaría buscando formas de dividir fragmentos en tipos más simples y más centrados, y luego encapsular las instancias de cada uno de esos conceptos en la clase compuesta.Por lo que se convierte en:

public MyLegacyType(SomeConcept foo, AnotherConcept bar, ...) 
{ 
} 

donde sólo la lógica necesaria para orquestar entre los conceptos permanece en MyLegacyType (ninguna lógica particular a SomeConcept va allí, etc).

Esto es diferente a "reducir los parámetros del constructor con tipos de contenedores que tienen propiedades para lo que solían ser argumentos de constructor", ya que estamos reestructurando la lógica fundamentalmente, no solo utilizando un objeto para reemplazar los argumentos del constructor.

+0

He examinado la composición aquí, pero en esencia, el tipo está haciendo una tarea: una representación de una entidad que resulta ser propiedad -pesado. Como dices, eso es por falta de contexto, veré si puedo enmendar mi pregunta con eso. –

+0

En realidad, teniendo en cuenta esta idea, si se lo pone de cabeza y se compone un elemento del contexto de la persona que llama, los argumentos del constructor se pueden cambiar para adaptarse. En un ejemplo, los elementos se construyen a partir de áreas DAL del código que proporcionan todas las propiedades sin falta. Estos podrían ser envueltos en un tipo de suministro que abastece todas las propiedades. Otras personas llaman a pretextos específicos, donde ciertas propiedades siempre son predeterminadas, estas podrían usar diferentes contenedores de constructor para ocultar este hecho. Estoy seguro de que debe haber un patrón para eso. –

4

Iría con los tipos de contenedor y usaría la asignación de propiedades inmediata de C# 4.0. De esta forma, uno podría usar Intellisense fácilmente en el tipo resultante, pero conservando un desacoplamiento decente del tipo original.

Por ejemplo:

public class MyLegacyType 
{ 
    public MyLegacyType(MyConfiguration configuration) // etc 
    { 
     // ... 
    } 
} 

public class MyConfiguration 
{ 
    public int Value1 { get; set; } 
    public int Value2 { get; set; } 
    // ... 
} 

Y luego:

var myInstance = new MyLegacyType(new MyConfiguration 
{ 
    Value1 = 123, 
    Value2 = 456 
}); 
1

Hay una cosa que no estoy seguro acerca de su pregunta, y es por eso qué quiere todos estos parámetros en el constructor ? ¿Estás usando todos los parámetros en el código del constructor? Tu problema con el intellisense probablemente proviene de tener demasiados parámetros en un solo método. Tener muchos campos/propiedades en un solo tipo no causará ningún problema.

Parece que ha visto algunas maneras de administrar el número de argumentos, pero si puede explicar por qué necesita recibirlos en un constructor, podemos pensar fuera de este cuadro. Puede haber algo allí para mirar.

+0

Uno de los motivos de todos los argumentos en el constructor, que actualmente es nuestro peor caso y el resultado de los estilos de codificación heredados, es que el código DAL puede construir el objeto desde un lector. No creo que haya otros motivos "suficientemente buenos" para ello, creo que es solo una víctima de personas que no refaccionan el código y simplemente lo agregan. La mayoría de las propiedades simplemente se transponen desde el constructor al campo interno. –

+0

¿Por qué no usa propiedades para rellenar los campos de entidad en DAL? O si necesita que esté en una sola instrucción C#, puede usar la sintaxis del inicializador de objetos en C# - 'new ClassName {Prop1 = value, Prop2 = value, ...}' – Iravanchi

+0

No todas las propiedades exponen a los setters. Creo que la única razón por la que se hace de esta manera es porque así es como siempre se ha hecho, nadie se ha encargado de cambiar el estilo. En realidad, uno de nuestros peores tipos es utilizado exclusivamente por el DAL, el único otro lugar al que se llama es en nuestros proyectos de prueba. –