Mi aplicación actual permite a los usuarios definir formularios web personalizados a través de un conjunto de pantallas de administración. es esencialmente una aplicación de tipo EAV. Como tal, no puedo codificar código HTML o ASP.NET para renderizar una página determinada. En cambio, la UI solicita una instancia de un objeto Form a partir de la capa de servicio, que a su vez construye una usando varias tablas RDMBS. Formulario contiene el tipo de clases que se puede esperar para ver en este contexto: Form
=>IEnumerable<FormSections>
=>IEnumerable<FormFields>
¿Es este un caso de uso típico para IOC?
es lo que se ve la capa de servicios como aquí:
public class MyFormService: IFormService{
public Form OpenForm(int formId){
//construct and return a concrete implementation of Form
}
}
Todo funciona espléndidamente (por un tiempo) . La interfaz de usuario no es más prudente sobre qué secciones/campos existen en una forma determinada: felizmente convierte el objeto Form que recibe en una página ASP.NET funcional.
Unas semanas más tarde, recibo un nuevo requerimiento del negocio: al ver versiones no editables (es decir, de solo lectura) de un formulario, ciertos valores de campo deben fusionarse y otros campos artificiales/calculados deben ser adicional. No hay problema, digo Basta con modificar mi clase de servicio para que sus métodos son más explícitos:
public class MyFormService: IFormService{
public Form OpenFormForEditing(int formId){
//construct and return a concrete implementation of Form
}
public Form OpenFormForViewing(int formId){
//construct and a concrete implementation of Form
//apply additional transformations to the form
}
}
nuevo todo funciona muy bien y el balance ha sido restaurado a la fuerza. La UI sigue siendo agnóstica en cuanto a lo que está en la Forma, y nuestra separación de preocupaciones se logra. Sin embargo, solo unas pocas semanas más tarde, la empresa presenta un nuevo requisito: en ciertos escenarios, deberíamos aplicar solo algunas de las transformaciones de formulario a las que hice referencia anteriormente.
En este punto, parece que el enfoque del "método explícito" ha llegado a un callejón sin salida, a menos que quiera terminar con una explosión de métodos (OpenFormViewingScenario1, OpenFormViewingScenario2, etc.). En su lugar, presento otro nivel de indirección:
public interface IFormViewCreator{
void CreateView(Form form);
}
public class MyFormService: IFormService{
public Form OpenFormForEditing(int formId){
//construct and return a concrete implementation of Form
}
public Form OpenFormForViewing(int formId, IFormViewCreator formViewCreator){
//construct a concrete implementation of Form
//apply transformations to the dynamic field list
return formViewCreator.CreateView(form);
}
}
En la superficie, esto parece como método aceptable y sin embargo, hay un cierto olor. A saber, la interfaz de usuario, que había estado viviendo en ignorante dicha sobre los detalles de implementación de OpenFormForViewing, debe poseer conocimiento y crear una instancia de IFormViewCreator.
- Mis preguntas son dos: ¿Hay un mejor manera de lograr el del composability que busco? (quizás por usando un contenedor IoC o una casa laminada de fábrica para crear el concreto IFormViewCreator )?
- ¿De verdad arruiné la abstracción de aquí?
+1 para la buena pregunta. – Lucero
Estoy de acuerdo contigo sobre el olor. Parece que la interfaz de usuario no debería tener que saber acerca de IFormViewCreator. Una pregunta para usted: ¿la interfaz de usuario tiene conocimiento de los parámetros que dictan el "sabor" picante que debería devolver? Podría tener más sentido que la UI pase un objeto que sirva de contenedor para los diversos valores que determinan qué transformaciones aplicar. Su método OpenFormForViewing() sería responsable de reenviar a la clase de fábrica, o explícitamente averiguar qué "sabores" devolver. –