¿MVVM es inútil? [cerrado]

91

¿Es inútil la implementación ortodoxa de MVVM? Estoy creando una nueva aplicación y consideré Windows Forms y WPF. Elegí WPF porque está preparado para el futuro y ofrece mucha flexibilidad. Hay menos código y es más fácil realizar cambios significativos en la interfaz de usuario con XAML.

Dado que la elección de WPF es obvia, pensé que también podría ir hasta el final utilizando MVVM como mi arquitectura de aplicación, ya que ofrece capacidad de mezcla, problemas de separación y capacidad de prueba de la unidad. En teoría, parece hermoso como el santo grial de la programación de UI. Esta breve aventura; sin embargo, se ha convertido en un verdadero dolor de cabeza. Como era de esperar en la práctica, descubrí que cambié un problema por otro. Tiendo a ser un programador obsesivo en el sentido de que quiero hacer las cosas de la manera correcta para poder obtener los resultados correctos y posiblemente convertirme en un mejor programador. ¡El patrón MVVM acaba de suspender mi prueba de productividad y acaba de convertirse en un gran truco asqueroso!

El caso claro es agregar soporte para un cuadro de diálogo modal. La forma correcta es colocar un cuadro de diálogo y vincularlo a un modelo de vista. Hacer que esto funcione es difícil. Para beneficiarse del patrón MVVM, debe distribuir código en varios lugares a lo largo de las capas de su aplicación. También debe usar construcciones de programación esotéricas como plantillas y expresiones lamba. Cosas que te hacen mirar la pantalla rascándote la cabeza. Esto hace que el mantenimiento y la depuración sean una pesadilla esperando suceder, como descubrí recientemente. Tuve un cuadro de aproximadamente funcionando bien hasta que obtuve una excepción la segunda vez que lo invoqué, diciendo que no podía mostrar el cuadro de diálogo nuevamente una vez que se cerró. Tuve que agregar un controlador de eventos para la funcionalidad de cierre a la ventana de diálogo, otro en la implementación de IDialogView del mismo y finalmente otro en el IDialogViewModel. ¡Pensé que MVVM nos salvaría de una piratería tan extravagante!

Hay varias personas por ahí con soluciones competitivas a este problema y todos son trucos y no brindan una solución limpia, fácil de reutilizar y elegante. La mayoría de los kits de herramientas de MVVM pasan por alto los diálogos y cuando los abordan, son solo cuadros de alerta que no requieren interfaces personalizadas o modelos de vista.

Estoy planeando renunciar al patrón de vista MVVM, al menos su implementación ortodoxa. ¿Qué piensas? ¿Ha valido la pena la molestia para usted si tuvo alguno? ¿Soy solo un programador incompetente o MVVM no es lo que se promociona?

Joel Rodgers
fuente
6
Siempre me he preguntado si MVVM es sobre ingeniería o no. Interesante pregunta.
Taylor Leese
11
Patrones como MVVM y MVC parecen sobre ingeniería, hasta que tienes que realizar algunas modificaciones o cambiar un componente. La primera vez que tienes que hacer eso, toda la ceremonia se amortiza.
Robert Harvey
41
¿Las lambdas son esotéricas? noticias para mi.
Ray Booysen
5
@Ray - ¡Jaja, +1 por ese comentario! : D
Venemo
7
Como señaló Alan Cooper hace más de una década en About Face , si está diseñando interfaces de usuario y los diálogos modales no son un caso marginal, probablemente esté haciendo algo mal.
Robert Rossney

Respuestas:

61

Perdón si mi respuesta se volvió un poco larga, ¡pero no me culpes! Tu pregunta también es extensa.

En resumen, MVVM no es inútil.

El caso claro es agregar soporte para un cuadro de diálogo modal. La forma correcta es colocar un cuadro de diálogo y vincularlo a un modelo de vista. Hacer que esto funcione es difícil.

Sí, realmente lo es.
Sin embargo, MVVM le proporciona una forma de separar la apariencia de la interfaz de usuario de su lógica. Nadie te obliga a usarlo en todas partes, y nadie sostiene un arma contra tu frente para hacerte crear un ViewModel separado para todo.

Aquí está mi solución para este ejemplo en particular: la
forma en que la interfaz de usuario maneja una determinada entrada no es asunto de ViewModel. Agregaría código al archivo .xaml.cs de View, que crea una instancia del cuadro de diálogo y establece la misma instancia de ViewModel (o algo más, si es necesario) como su DataContext.

Para beneficiarse del patrón MVVM, debe distribuir código en varios lugares a lo largo de las capas de su aplicación. También debe usar construcciones de programación esotéricas como plantillas y expresiones lamba.

Bueno, no tienes que usarlo en varios lugares. Así es como lo resolvería:

  • Agregue el XAML a la vista y nada en el .xaml.cs
  • Escriba la lógica de cada aplicación (excepto las cosas que operarían directamente con elementos de la interfaz de usuario) dentro de un ViewModel
  • Todo el código que debe realizar la interfaz de usuario, pero que no tiene nada que ver con la lógica empresarial, va a los archivos .xaml.cs

Creo que el propósito de MVVM es principalmente separar la lógica de la aplicación y la interfaz de usuario concreta, permitiendo así modificaciones fáciles (o reemplazo completo) de la interfaz de usuario.
Utilizo el siguiente principio: la Vista puede saber y asumir todo lo que quiera del ViewModel, pero el ViewModel no puede saber NADA sobre la Vista.
WPF proporciona un buen modelo de enlace que puede usar para lograr exactamente eso.

(Por cierto, las plantillas y las expresiones lambda no son esotéricas si se usan correctamente. Pero si no quieres, no las uses).

Cosas que te hacen mirar la pantalla rascándote la cabeza.

Sí, conozco el sentimiento. Exactamente lo que estaba sintiendo cuando vi MVVM por primera vez. Pero una vez que aprendas a hacerlo, ya no te sentirás mal.

Tenía una caja de aproximadamente funcionando bien ...

¿Por qué pondrías un ViewModel detrás de un cuadro de información? No tiene sentido eso.

La mayoría de los kits de herramientas de MVVM pasan por alto los diálogos y cuando los abordan, son solo cuadros de alerta que no requieren interfaces personalizadas o modelos de vista.

Sí, porque el mero hecho de que un elemento de la interfaz de usuario esté en la misma ventana, o en otra ventana, o esté orbitando Marte en este momento, no es asunto de ViewModels.
Separación de intereses

EDITAR:

Aquí hay un video muy bueno cuyo título es Construya su propio marco MVVM . Vale la pena verlo.

Venemo
fuente
2
+1 para las últimas tres palabras. Pero el resto de la respuesta también es buena. :)
Robert Harvey
12
+1 para obtener consejos sobre el uso de código subyacente. Es un error común pensar que es "malo" usar código subyacente en MVVM ... pero para cosas puramente relacionadas con la interfaz de usuario, ese es el camino a seguir.
Thomas Levesque
@Thomas: Sí, no podría estar más de acuerdo. He visto varias implementaciones en las que la gente pone todo el código (incluso el relacionado con la interfaz de usuario) en ViewModel, porque (según ellos) "ahí es donde está el código". Fue bastante hacky.
Venemo
3
@Venemo, creo que puedes encapsular muchas de las cosas que querrías poner en el código subyacente usando técnicas como comportamientos personalizados, que es útil si te encuentras escribiendo repetidamente código de pegamento. Sin embargo, en general, creo que es mejor usar código subyacente para pegar que piratear XAML incómodo. En mi opinión, la principal preocupación es asegurarse de que no haya nada en el código subyacente lo suficientemente sofisticado como para justificar las pruebas unitarias. Cualquier cosa lo suficientemente compleja está mejor encapsulada en ViewModel o en una clase de extensión, como Behavior o MarkupExtension.
Dan Bryant
7
@ Thomas: Tienes razón, el mayor mito sobre MVVM es que el propósito de MVVM es deshacerse del código subyacente. El propósito es eliminar el ocde no UI del código subyacente. Poner código solo de IU en ViewModel es tan malo como poner código de dominio problemático en el código subyacente.
Jim Reineri
8

Hacer que esto funcione es difícil. Para beneficiarse del patrón MVVM, debe distribuir código en varios lugares a lo largo de las capas de su aplicación. También debe usar construcciones de programación esotéricas como plantillas y expresiones lamba.

¿Para un cuadro de diálogo modal común? Ciertamente está haciendo algo mal allí: la implementación de MVVM no tiene por qué ser tan compleja.

Teniendo en cuenta que es nuevo tanto en MVVM como en WPF, es probable que esté utilizando soluciones subóptimas en todas partes y que complique las cosas innecesariamente, al menos lo hice cuando fui a WPF por primera vez. Asegúrese de que el problema sea realmente MVVM y no su implementación antes de darse por vencido.

MVVM, MVC, Document-View, etc. es una vieja familia de patrones. Hay inconvenientes, pero no fallas fatales como las que usted describe.

ima
fuente
5

Estoy en medio de un desarrollo MVVM bastante complejo usando PRISM, así que ya tuve que hacer frente a este tipo de preocupaciones.

Mis conclusiones personales:

MVVM vs MVC / PopUps & co

  • MVVM es realmente un gran patrón y en la mayoría de los casos reemplaza completamente MVC gracias al poderoso enlace de datos en WPF
  • Llamar a su capa de servicio directamente desde el presentador es una implementación legítima en la mayoría de los casos
  • Incluso los escenarios de Lista / Detalle bastante complejos pueden implementarse mediante MVVM puro gracias a la sintaxis {Binding Path = /}
  • No obstante, cuando es necesario implementar una coordinación compleja entre múltiples vistas, un controlador en
  • Se pueden utilizar eventos; el patrón antiguo que implica almacenar instancias de IView (o AbstractObserver) en el controlador es obsoleto
  • El controlador se puede inyectar en cada contenedor de Presentador por IOC
  • El servicio IEventAggregator de Prism es otra posible solución si el único uso del controlador es el envío de eventos (en este caso, puede reemplazar completamente el controlador)
  • Si las vistas deben crearse dinámicamente, este es un trabajo muy adecuado para el controlador (en el prisma, el controlador se inyectará (IOC) un IRegionManager)
  • Los cuadros de diálogo modales son en su mayoría obsoletos en las aplicaciones compuestas modernas, excepto para las operaciones de bloqueo real como las confirmaciones obligatorias; en estos casos, la activación modal puede abstraerse como un servicio llamado dentro del controlador e implementarse por una clase especializada, lo que también permite pruebas unitarias avanzadas a nivel de presentación. El controlador, por ejemplo, llamará a IConfirmationService.RequestConfirmation ("estás seguro") que activará una pantalla de diálogo modal en tiempo de ejecución y se puede burlar fácilmente durante la prueba de la unidad
dan ionescu
fuente
5

Me ocupo del problema de los diálogos haciendo trampa. My MainWindow implementa una interfaz IWindowServices que expone todos los diálogos específicos de la aplicación. Mis otros ViewModels pueden importar la interfaz de servicios (yo uso MEF, pero fácilmente podría pasar la interfaz a través de constructores manualmente) y usarla para lograr lo que sea necesario. Por ejemplo, así es como se ve la interfaz para una pequeña aplicación de utilidad mía:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

Esto coloca todas las ejecuciones de Dialog en un solo lugar y se puede eliminar fácilmente para pruebas unitarias. Sigo el patrón que tiene el cliente del cuadro de diálogo para crear el ViewModel apropiado, que luego pueden configurar según sea necesario. La llamada Execute bloquea y luego el cliente puede mirar el contenido del ViewModel para ver los resultados del diálogo.

Un diseño MVVM más 'puro' puede ser importante para una aplicación grande, donde se necesita un aislamiento más limpio y una composición más compleja, pero para las aplicaciones pequeñas y medianas, creo que un enfoque práctico, con los servicios adecuados para exponer los ganchos necesarios, es suficiente. .

Dan Bryant
fuente
¿Pero dónde lo llamas? ¿No sería mejor simplemente construir el diálogo dentro de las funciones de la clase IWindowServices en lugar de pasarlo? De esa forma, la vista del modelo de llamada no tendría que saber nada sobre la implementación del diálogo en particular.
Joel Rodgers
La interfaz se inyecta en cualquiera de mis instancias de ViewModel que necesite acceso a los diálogos de la aplicación. En la mayoría de los casos, paso el ViewModel para el cuadro de diálogo, pero me volví un poco perezoso y utilicé WPF OpenFileDialog y SaveFileDialog para las llamadas de diálogo de archivos. Mi objetivo principal era el aislamiento a los efectos de las pruebas unitarias, por lo que esto es suficiente para ese objetivo. Si desea un mejor aislamiento, probablemente desee crear un OpenFileViewModel y SaveFileViewModel, que duplicaría las propiedades necesarias para los diálogos.
Dan Bryant
Tenga en cuenta que este definitivamente no es un enfoque puro, ya que el ViewModel que usa los cuadros de diálogo conoce el ViewModel específico para cada cuadro de diálogo que desea abrir. Creo que esto está bastante limpio, pero siempre puede agregar una capa adicional de aislamiento con una clase que exponga puramente los parámetros necesarios para el uso del diálogo, ocultando cualquier visibilidad innecesaria de las propiedades de ViewModel utilizadas durante el enlace. Para aplicaciones más pequeñas, creo que este aislamiento adicional es excesivo.
Dan Bryant
5

Los patrones de diseño están ahí para ayudarte, no para obstaculizar. Una pequeña parte de ser un buen desarrollador es saber cuándo "romper las reglas". Si MVVM es engorroso para una tarea y ha determinado que el valor futuro no vale la pena, no utilice el patrón. Por ejemplo, como han comentado otros carteles, ¿por qué pasaría por todos los gastos generales para implementar un cuadro de información simple?

Los patrones de diseño nunca tuvieron la intención de ser seguidos dogmáticamente.

RMart
fuente
2
Correcto. Un cuadro simple debería ser simple, pero ¿qué sucede si tiene que mostrar información como la versión, la licencia, el proceso en ejecución, el nombre de la empresa, etc.? Esta es toda la información que vive en algún lugar. En los formularios estándar, puede vincular toda esa información y terminar con ella. MVVM dice que debe crear un modelo de vista para él, que es redundante.
ATL_DEV
1

Como el patrón en sí MVVM es genial. Pero la biblioteca de control de WPF enviada con soporte de enlace de datos NET 4.0 es muy limitada, es mucho mejor que WinForm, pero aún no es suficiente para MVVM enlazable, yo diría que su potencia es aproximadamente el 30% de lo que se necesita para MVVM enlazable.
MVVM enlazable: es una interfaz de usuario donde ViewModel está conectado con View solo mediante el enlace de datos.
El patrón MVVM trata sobre la representación de objetos de ViewState, no describe cómo mantiene la sincronización entre View y ViewModel, en WPF es el enlace de datos pero puede ser cualquier cosa. Y, de hecho, puede usar el patrón MVVM en cualquier kit de herramientas de IU que admita eventos / devoluciones de llamada, puede usarlo en WinAPI puro en WinForms (lo hice, y no es mucho más trabajo con eventos / devoluciones de llamada), e incluso puede usarlo en texto Consola, como reescribir Norton Commander de DoS usando el patrón MVVM.

En resumen: MVVM no es inútil, es genial. La biblioteca de control de NET 4.0 WPF es basura.

Aquí está la simple prueba de concepto ViewModel que no puede vincular de forma puramente MVVM usando WPF.

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

No puede vincular datos de los encabezados de columna DataGrid de WPF, no puede vincular datos de filas seleccionadas, etc., etc., lo hará en forma simple de código o escribirá 200 líneas de código de pirateo XAML para estas 5 líneas de ViewModel más simple. Solo puede imaginar cómo empeoran las cosas con ViewModels complejos.
Entonces, la respuesta es simple, a menos que esté escribiendo la aplicación Hello World, usar MVVM enlazable en WPF no tiene sentido. Pasará la mayor parte de su tiempo pensando en piratear para vincular su ViewModel. El enlace de datos es bueno, pero prepárese para recurrir al 70% del tiempo del evento.

Alex Burtsev
fuente
Puede vincular esto, con convertidores, a un DataGrid.
Cameron MacFarland
@CameronMacFarland: No todas, algunas propiedades son de solo lectura y no se pueden enlazar, algunas simplemente no existen y solo hay eventos que informan cambios de estado.
Alex Burtsev
Admito que no tengo mucha experiencia en el uso de WPF DataGrid. Tiendo a evitarlo porque es feo y ya no encaja con WPF. Dicho esto, una combinación de convertidores y AttachedProperties para manejar eventos debería proporcionarle lo que necesita.
Cameron MacFarland
1
Alex, los problemas que tienes están relacionados con el diseño de DataGrid, no con MVVM. Es simplemente incorrecto decir que "la vinculación de datos es buena, pero prepárese para recurrir al 70% del tiempo del evento". He escrito algunas aplicaciones WPF objetivamente enormes en las que no hay controladores de eventos en la interfaz de usuario en absoluto, con la excepción del controlador de eventos que la cuadrícula de datos (Telerik) necesita para la inicialización.
Robert Rossney
3
Creo que podrías tener más éxito si en lugar de adoptar la actitud "Esto está mal diseñado y no funciona", intentaste "¿Por qué funciona esto para otras personas pero no para mí?" Es posible que descubra que la razón por la que las cosas son difíciles de hacer es que aún no sabe cómo hacerlo.
Robert Rossney
0

No, no es inútil, pero es difícil entenderlo a pesar de que el patrón en sí es ridículamente simple. Hay toneladas de información errónea y varios grupos que luchan por encontrar la forma correcta. Creo que con WPF y Silverlight deberías usar MVVM o estarás sobre codificando e intentando resolver problemas en un nuevo modelo, la “vieja” metodología de formularios win que te lleva a problemas. Este es más el caso en Silverlight, ya que se requiere que todo sea asíncrono (es posible realizar hacks en torno a esto, pero solo debe elegir otra plataforma).

Sugeriría leer este artículo Simplificar WPF TreeView usando el patrón ViewModel con cuidado para ver cómo MVVM se puede implementar bien y permitirle cambiar su mentalidad de formularios ganadores a la nueva forma de pensar en MVVM. En resumen, cuando desee hacer algo, aplique la lógica al ViewModel primero, no a la Vista. ¿Quieres seleccionar un artículo? ¿Cambiar un icono? No iterar sobre los elementos de la interfaz de usuario, solo actualice las propiedades de los modelos y deje que el enlace de datos haga el meollo del asunto.

RemolcadorCapitán
fuente
-1

He visto el mismo problema con muchas implementaciones de MVVM cuando se trata de diálogos (modales). Cuando miro a los participantes del patrón MVVM, tengo la sensación de que falta algo para construir una aplicación coherente.

  • Ver contiene los controles específicos de la GUI y define la apariencia de la interfaz de usuario.
  • ViewModel representa el estado y el comportamiento de la presentación.
  • El modelo puede ser un objeto comercial de la capa de dominio o un servicio que proporciona los datos necesarios.

Pero falta es:

  • ¿Quién crea los ViewModels?
  • ¿Quién es responsable del flujo de trabajo de la aplicación?
  • ¿Quién media entre ViewModels cuando necesitan comunicarse entre sí?

Mi enfoque es introducir un controlador (caso de uso) que es responsable de los puntos faltantes. Cómo funciona esto se puede ver en las aplicaciones de muestra de WPF Application Framework (WAF) .

jbe
fuente
El patrón de Mediador implementado por Josh Smith resolvió todos los problemas de comunicación de mi modelo de vista. Messenger.NotifyColleagues proporcionó una manera de tener modelos de vista completamente independientes que sabían cómo responder a los eventos globales (si les importaba) sin que dos modelos de vista se conocieran entre sí. Ya nos ha salvado el tocino unas cuantas veces.
JasonD