15

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.

  1. 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 )?
  2. ¿De verdad arruiné la abstracción de aquí?
+2

+1 para la buena pregunta. – Lucero

+0

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. –

Respuesta

9

Como tengo entendido la pregunta, debe modificar el formulario antes de enviarlo a la capa de la interfaz de usuario. Eso me parece que un Decorator estaría en su lugar. Mantenga la interfaz IFormService anterior sin IFormViewCreator.

Ahora puede crear uno o más Servicios de formularios de decoración que implementen el filtrado o la modificación deseados.

public class MyDecoratingFormService : IFormService 
{ 
    private readonly IFormService formService; 

    public MyDecoratingFormService(IFormService formService) 
    { 
     if(formService == null) 
     { 
      throw new ArgumentNullException("formService"); 
     } 

     this.formService = formService; 
    } 

    public Form OpenFormForEditing(int formId) 
    { 
     var form = this.formService.OpenFormForEditing(formId); 
     return this.TransformForm(form); 
    } 

    public Form OpenFormForViewing(int formId) 
    { 
     var form = this.formService.OpenFormForViewing(formId); 
     return this.TransformForm(form); 
    } 

    public Form TransformForm(Form form) 
    { 
     // Implement transformation/filtering/modification here 
    } 
} 

Ahora puede decorar su implementación original de IFormService con uno o más de dichos decoradores.

IFormService formService = new MyDecoratingFormService(new MyFormService()); 

Puede envolver tantos decoradores (cada uno con su propia responsabilidad) entre sí como desee.

No hay una necesidad explícita de un contenedor DI para hacer esto, pero se adapta muy bien a otros patrones DI. Yo uso decorador todo el tiempo :)

+0

+1 para una buena respuesta. Espero generar más discusión antes de otorgar una respuesta. –

+0

@Dirk: si crees que es una buena respuesta, ¿por qué no aceptas la respuesta? –

+0

Porque SO no me deja. No hay marca de "aceptación" disponible. ¿Es eso porque puse una recompensa en esta pregunta? –

0

A menos que yo quiero terminar con una explosión de métodos (OpenFormViewingScenario1, OpenFormViewingScenario2, etc).

No se puede quitar la inherente complejidad de los requisitos. De una forma u otra, deberá detectar el caso de uso y actuar en consecuencia.

Puede enumerar todas las combinaciones posibles o tratar de factorizar el procesamiento de forma inteligente, si es posible. Por ejemplo, si debido a la naturaleza del requisito hay N * M = X posibilidades, entonces tener una lista exhaustiva de los métodos X no es bueno. Debe factorizar así que debe crear los formularios posibles X a partir de una composición de los casos N y M.

Sin saber el requisito exacto es difícil de decir. Entonces hay muchas formas posibles de factorizar tal composición, p. Decorador, ChainOfResponsability, Filtro, etc. Todas estas son formas de componer una lógica compleja.

A saber, la interfaz de usuario, que había estado viviendo en la dicha ignorante acerca de los detalles de implementación de OpenFormForViewing, debe poseer conocimientos de y crear una instancia de IFormViewCreator.

La presentación debe entregarse en un formulario creado en otro lugar, a fin de permanecer independiente de la creación del formulario. Sin embargo, el formulario deberá ser ensamblado/creado en algún lugar.

En MVC, dicha lógica va en el controlador que actualizaría el modelo/formulario y se enviaría a la vista/representación correcta. Lo mismo se puede hacer con ASP.NET

Cuestiones relacionadas