¿Cómo configuro MVP para una solución Winforms?

14

He usado MVP y MVC en el pasado, y prefiero MVP ya que controla el flujo de ejecución mucho mejor en mi opinión.

He creado mi infraestructura (clases de almacén de datos / repositorio) y las uso sin problemas al codificar datos de muestra, por lo que ahora me estoy moviendo a la GUI y preparando mi MVP.

Sección a

  1. He visto a MVP usar la vista como punto de entrada, es decir, en el método del constructor de vistas crea el presentador, que a su vez crea el modelo, conectando los eventos según sea necesario.

  2. También he visto al presentador como el punto de entrada, donde se crean una vista, modelo y presentador, a este presentador se le da una vista y un objeto modelo en su constructor para conectar los eventos.

  3. Como en 2, pero el modelo no se pasa al presentador. En cambio, el modelo es una clase estática donde se llaman métodos y se devuelven respuestas directamente.

Sección B

En términos de mantener la vista y el modelo sincronizados que he visto.

  1. Siempre que cambie un valor en la vista, es decir, TextChangedevento en .Net / C #. Esto activa un elemento DataChangedEventque se pasa al modelo para mantenerlo sincronizado en todo momento. Y cuando el modelo cambia, es decir, un evento de fondo que escucha, la vista se actualiza mediante la misma idea de generar un DataChangedEvent. Cuando un usuario desea confirmar los cambios, SaveEventse activa y pasa al modelo para guardarlos. En este caso, el modelo imita los datos de la vista y procesa acciones.

  2. Similar a # b1, sin embargo, la vista no se sincroniza con el modelo todo el tiempo. En cambio, cuando el usuario quiere confirmar los cambios, SaveEventes despedido y el presentador toma los últimos detalles y los pasa al modelo. en este caso, el modelo no conoce los datos de las vistas hasta que se requiere que actúe sobre ellos, en cuyo caso se pasan todos los detalles necesarios.

Sección C

Visualización de objetos comerciales en la vista, es decir, un objeto (MyClass) no datos primitivos (int, double)

  1. La vista tiene campos de propiedad para todos sus datos que se mostrarán como objetos de dominio / negocios. Tal como view.Animalsexpone una IEnumerable<IAnimal>propiedad, aunque la vista los procese en Nodos en un TreeView. Luego para el animal seleccionado se expondría SelectedAnimalcomo IAnimalpropiedad.

  2. La vista no tiene conocimiento de los objetos de dominio, expone la propiedad solo para los tipos de objetos incluidos primitivo / framework (.Net / Java). En este caso, el presentador pasará un objeto adaptador al objeto de dominio, luego el adaptador traducirá un objeto comercial dado a los controles visibles en la vista. En este caso, el adaptador debe tener acceso a los controles reales en la vista, no cualquier vista, por lo que se acopla más estrechamente.

Sección D

Múltiples vistas utilizadas para crear un solo control. es decir, tiene una vista compleja con un modelo simple como guardar objetos de diferentes tipos. Puede tener un sistema de menús a un lado con cada clic en un elemento que muestra los controles apropiados.

  1. Crea una vista enorme, que contiene todos los controles individuales que se exponen a través de la interfaz de vistas.

  2. Tienes varias vistas. Tiene una vista para el menú y un panel en blanco. Esta vista crea las otras vistas requeridas pero no las muestra (visible = false), esta vista también implementa la interfaz para cada vista que contiene (es decir, vistas secundarias) para que pueda exponerse a un presentador. El panel en blanco se llena con otras vistas ( Controls.Add(myview)) y ( (myview.visible = true). Los eventos generados en estas vistas "secundarias" son manejados por la vista principal, que a su vez pasa el evento al presentador, y viceversa para proporcionar eventos a elementos secundarios.

  3. Cada vista, ya sea la vista principal principal o secundaria, está conectada a su propio presentador y modelo. Literalmente, puede soltar un control de vista en un formulario existente y tendrá la funcionalidad lista, solo necesita conectarse a un presentador detrás de escena.

Sección E

Si todo tiene una interfaz, ahora, según la forma en que se haga el MVP en los ejemplos anteriores, afectará esta respuesta, ya que podrían no ser compatibles.

  1. Todo tiene una interfaz, la vista, el presentador y el modelo. Cada uno de estos obviamente tiene una implementación concreta. Incluso si solo tiene una vista concreta, modelo y presentador.

  2. La Vista y el Modelo tienen una interfaz. Esto permite que las vistas y los modelos difieran. El presentador crea / recibe objetos de vista y modelo y solo sirve para pasar mensajes entre ellos.

  3. Solo la Vista tiene una interfaz. El modelo tiene métodos estáticos y no se crea, por lo que no se necesita una interfaz. Si desea un modelo diferente, el presentador llama a un conjunto diferente de métodos de clase estática. Siendo estático, el modelo no tiene ningún enlace con el presentador.

Pensamientos personales

De todas las diferentes variaciones que he presentado (la mayoría probablemente las he usado de alguna forma) de las cuales estoy seguro de que hay más. Prefiero A3 porque mantiene la lógica de negocios reutilizable fuera de solo MVP, B2 para menos duplicación de datos y menos eventos que se disparan. C1 por no agregar en otra clase, seguro que pone una pequeña cantidad de lógica no comprobable en la unidad en una vista (cómo se visualiza un objeto de dominio) pero esto podría revisarse en código o simplemente verse en la aplicación. Si la lógica fuera compleja, estaría de acuerdo con una clase de adaptador, pero no en todos los casos. Para la sección D, creo que D1 crea una vista que es demasiado grande al menos para un ejemplo de menú. He usado D2 y D3 antes. El problema con D2 es que tiene que escribir mucho código para enrutar eventos hacia y desde el presentador a la vista secundaria correcta, y no es compatible con arrastrar / soltar, cada nuevo control necesita más cableado para admitir el único presentador. D3 es mi opción preferida, pero agrega aún más clases como presentadores y modelos para lidiar con la vista, incluso si la vista es muy simple o no necesita ser reutilizada. Creo que una combinación de D2 y D3 se basa mejor en las circunstancias. En cuanto a la sección E, creo que todo lo que tiene una interfaz podría ser excesivo. Ya lo hago para objetos de dominio / negocios y, a menudo, no veo ninguna ventaja en el "diseño" al hacerlo, pero ayuda a burlarse de los objetos en las pruebas. Personalmente, vería E2 como una solución clásica, aunque he visto E3 utilizado en 2 proyectos en los que he trabajado anteriormente. Creo que una combinación de D2 y D3 se basa mejor en las circunstancias. En cuanto a la sección E, creo que todo lo que tiene una interfaz podría ser excesivo. Ya lo hago para objetos de dominio / negocios y, a menudo, no veo ninguna ventaja en el "diseño" al hacerlo, pero ayuda a burlarse de los objetos en las pruebas. Personalmente, vería E2 como una solución clásica, aunque he visto E3 utilizado en 2 proyectos en los que he trabajado anteriormente. Creo que una combinación de D2 y D3 se basa mejor en las circunstancias. En cuanto a la sección E, creo que todo lo que tiene una interfaz podría ser excesivo. Ya lo hago para objetos de dominio / negocios y, a menudo, no veo ninguna ventaja en el "diseño" al hacerlo, pero ayuda a burlarse de los objetos en las pruebas. Personalmente, vería E2 como una solución clásica, aunque he visto E3 utilizado en 2 proyectos en los que he trabajado anteriormente.

Pregunta

¿Estoy implementando MVP correctamente? ¿Hay una manera correcta de hacerlo?

Leí el trabajo de Martin Fowler que tiene variaciones, y recuerdo que cuando comencé a hacer MVC, entendí el concepto, pero originalmente no pude averiguar dónde está el punto de entrada, todo tiene su propia función, pero controla y crea el original. conjunto de objetos MVC.

JonWillis
fuente
2
La razón para hacer esta pregunta es que estoy buscando hacerlo bien en casi el primer intento. Preferiría tener un MVP estándar para seguir, en lugar de crear 6 aplicaciones que usen diferentes variaciones para el mismo patrón.
JonWillis
Perfecto ... quería preguntarlo por mucho tiempo
The King

Respuestas:

4

Mucho de lo que presentas aquí es muy razonable y sólido. Algunas de las opciones dependerán de detalles específicos de la aplicación y cuál "se siente" bien. Como es el caso la mayoría de las veces, no habrá una respuesta correcta. Algunas de las opciones tendrán sentido aquí y esas opciones podrían estar completamente equivocadas para la próxima aplicación y circunstancias. Sin conocer algunos de los detalles de la aplicación, creo que está en el camino correcto y ha tomado algunas decisiones sensatas y reflexivas.

Para mí, siento que el presentador casi siempre debería ser el punto de entrada. Tener la IU como punto de entrada pone demasiada lógica en la IU y le quita la capacidad de sustituir una nueva IU sin grandes cambios de codificación. Y realmente ese ES EL trabajo del presentador.

Walter
fuente
Tal vez la pregunta que debería hacer es alguna de las formas de usar MVP simplemente equivocada. Estoy de acuerdo con las variaciones que se adaptan a diferentes escenarios, pero creo que debería haber un enfoque generalmente aceptado. Los ejemplos de MVP son todos ejemplos simples y casi todos con la misma necesidad, para editar un objeto de dominio y guardar cambios. Sin embargo, a partir de esos ejemplos con el mismo objetivo, se han producido las variaciones anteriores. Acabo de codificar parte de una aplicación que utiliza eventos activados en la vista para ser manejados en el presentador, pero podría haber hecho que la vista tuviera una referencia al presentador y llamara directamente a los métodos.
JonWillis
Estoy de acuerdo con todo el esfuerzo en simplificar la vista para justificar que no se esté probando la unidad, entonces el presentador debe tener el control. Mi única preocupación con esto (pensar en mi cabeza que podría estar equivocado) es decir que es posible que winforms / controles de usuario tengan que estar vinculados a elementos principales y me pregunto si se necesita lógica adicional en un presentador para escribirlo.
JonWillis
@ Jon - ¿Cómo se equivocan los MVP? En mi libro sería cuando la vista sepa sobre el presentador. Puede que no esté "equivocado" en el sentido de que funciona, pero simplemente no sería MVP, se transformó en otra cosa en ese momento. El problema con los patrones de diseño es que los ejemplos son siempre tan limpios y simples como sea posible. Luego, cuando va a implementarlos por primera vez y el mundo real salta y dice 'hey, las aplicaciones reales son mucho más complicadas que ese ejemplo insignificante'. Ahí es donde comienza tu crecimiento. Encuentra lo que funciona para ti y las circunstancias de la aplicación.
Walter
gracias por el consejo. Recuerdo haber aprendido MVC en la universidad. Sonaba genial, pero con un lienzo en blanco te preguntas por dónde empezar y cómo funciona realmente. En términos de que MVP está equivocado, quiero decir que alguna de las ideas / variaciones de MVC que publiqué arriba fue la forma incorrecta de hacerlo, es decir, cuando la vista sabe cómo funciona el presentador. O la vista debe tener una referencia a una interfaz de un presentador o tipo concreto, etc. Solo hay muchas variaciones diferentes que pueden funcionar para el mismo propósito.
JonWillis
1
@ Jon - Tus variaciones están más o menos en consonancia con el espíritu de MVP. En cuanto a trabajar con interfaces o tipos concretos, dependerá de las circunstancias de la aplicación. Si la aplicación es bastante pequeña, no muy compleja, quizás no sea necesario agregar interfaces. Me gusta mantener las cosas lo más simples posible, hasta que quede claro que la aplicación necesita implementar la arquitectura X. Consulte esta respuesta para obtener más detalles: programmers.stackexchange.com/questions/34547/…
Walter
4

Utilizamos una forma modificada de MVP en nuestra aplicación .NET 2.0 Winforms. Las dos piezas que nos faltaban eran un adaptador modificado del WPF ViewModel y agregar enlace de datos. Nuestro patrón específico es MVPVM.

Nos conectamos como presentadores primero en casi todos los casos, excepto en los controles de usuario personalizados, que están conectados en vista primero para facilitar el diseño. Utilizamos inyección de dependencia, ViewModels generados por código, BDD para los presentadores y TDD / TED para el modelo.

Las máquinas virtuales son solo una gran cantidad de propiedades planas que aumentan PropertyChanged cuando se cambian. Fue muy fácil generarlos por código (y sus pruebas de unidad de ejercicio asociadas). Los usamos para leer y escribir en controles interactivos con el usuario y para controlar estados habilitados. El modelo de vista se combina con la vista, ya que utilizamos el enlace de datos para casi todo lo demás.

La Vista ocasionalmente tendrá métodos para lograr cosas que la VM no puede. Esto generalmente controla la visibilidad de los elementos (WinForms puede ser exigente al respecto) y las cosas que se niegan a estar vinculadas a los datos. La Vista siempre expone eventos como "Iniciar sesión" o "Reiniciar", con EventArgs apropiados para actuar sobre los comportamientos del usuario. A menos que hayamos tenido que usar un truco como "View.ShowLoginBox", la Vista es completamente intercambiable siempre que cumpla con los requisitos generales de diseño.

Nos tomó unos 6-8 meses para concretar este patrón. Tiene muchas piezas, pero es muy flexible y extremadamente potente. Nuestra implementación específica es muy asíncrona e impulsada por eventos, lo que puede ser un artefacto de otros requisitos en lugar de un efecto secundario del comportamiento del diseño. Por ejemplo, agregué sincronización de subprocesos a la clase base de la que heredan nuestras máquinas virtuales (que simplemente expuso un método OnPropertyChanged para generar el evento), y ahora, podemos tener presentadores y modelos multiproceso.

Bryan Boettcher
fuente
Sé que su MVPVM puede ser de interés comercial, pero si está bien, ¿podría proporcionar algún código de muestra? En una nota al margen, ¿dónde trazas la línea sobre lo que hace en el modelo y lo que pasa en el presentador? He visto que el presentador es tan básico que maneja los eventos de visualización y simplemente llama al modelo, al presentador que accede a las capas de datos para pasar objetos de negocio a un modelo, hasta el modelo reemplazado por el presentador.
JonWillis
Sí, déjame terminar un ejemplo. Se adaptará ligeramente a nuestro negocio, ya que lo estoy usando como una herramienta de capacitación interna.
Bryan Boettcher
Gracias, lo
revisaré
@insta: el enlace está inactivo, ¿podría volver a cargarlo en alguna parte?
Yves Schelpe
1
Mierda, lo eliminé como hace una semana. Cifras :(
Bryan Boettcher
2

Estoy usando una versión de PureMvc que ha sido modificada para .Net y luego la extendí yo mismo.

Estaba acostumbrado a PureMvc de usarlo en aplicaciones Flex. Es un tipo de estructura básica, por lo que es bastante fácil de adaptar si desea personalizarlo.

Me tomé las siguientes libertades:

  • Utilizando la reflexión pude obtener propiedades privadas en la vista mediadora para salir a la clase de formulario y tomar la referencia (privada) de cada control que estaba mediando.
  • Utilizando la reflexión, puedo conectar automáticamente los controles a las firmas de eventos en mi mediador, que son genéricos, así que solo enciendo el parámetro del remitente.
  • Normalmente, PureMvc quiere que los mediadores derivados definan sus intereses de notificación en una función que devolverá una serie de cadenas. Como mis intereses son mayormente estáticos y quería tener una manera más fácil de ver los intereses de los mediadores, hice una modificación para que el mediador también pueda declarar sus intereses mediante un conjunto de variables miembro con una firma particular: _ _ _ <classname> _ <nombre de notificación>. De esta manera puedo ver lo que está sucediendo usando el árbol de miembros IDE en lugar de mirar dentro de una función.

En PureMvc, puede usar un comando como punto de entrada, un comando de inicio típico configuraría el modelo en la medida de lo posible, luego crearía el formulario principal, crearía y registraría el mediador para y con ese formulario, y luego haría Application.Run en forma.

El mediador para el formulario sería responsable de configurar todos los sub-mediadores, algo de esto puede automatizarse, nuevamente, utilizando trucos de reflexión.

El sistema que estoy usando es compatible con arrastrar / soltar, si entiendo su significado. El formulario real se creó en VS, pero mi experiencia es solo con formularios que tienen controles creados estáticamente. Cosas como elementos de menú creados dinámicamente parecen factibles con algunos ajustes del mediador para ese menú o submenú. Donde se pondría difícil es cuando el mediador no tenía un elemento raíz estático para engancharse y usted se metió en la creación de mediadores dinámicos de 'instancia'.

marca
fuente