2010-02-24 13 views
8

Quizás he estado haciendo el desarrollo de Flex con Frameworks como Cairngorm demasiado tiempo, pero todavía no obtengo MVVM. Soy consciente de que Cairngorm es un framework y MVVM es un patrón de diseño, pero lo que estoy comparando aquí son las implementaciones de Cairngorms de patrones de diseño, principalmente el controlador de vista modelo y el patrón de comando. No me malinterpreten, creo que la idea de vincular una vista a un modelo de vista es excelente y las ventajas en la capacidad de prueba y el flujo de trabajo del programador diseñador son excelentes. Pero hay dos cosas que me molestan: una es programar todas mis acciones con Commands, que, por cierto, también me ha dejado fuera de Cairngorm. Solo en Cairngorm la forma en que implementaron el patrón de comando te dio la ventaja de tener un controlador centralizado para todos tus comandos, que no pareces obtener con MVVM, a menos que me falta algo. Y si pensé que la implementación de los comandos en Cairngorm era intrincada en MVVM es mucho peor, me refiero a tener que crear clases privadas que implementen ICommand para todo lo que hago parece demasiado. Y luego tiene el problema de que no todos los controles implementan comandos por lo que, por ejemplo, si está usando un ListBox, que uso mucho, no tiene suerte; hay soluciones pero todo complicado.¡Todavía no consigo MVVM!

La otra cosa que me molesta es la comunicación entre View Models. En un Controlador de Vista de Modelos estándar usted recopila toda su información en un modelo centralizado que las vistas observan, pero este no parece ser el caso con MVVM, al menos no en los ejemplos que he visto. Entonces, por ejemplo, si tiene un control con una lista que usa para seleccionar un elemento que luego se usa como fuente para diferentes vistas y acciones consiguientes, no me queda claro cómo notifica a todos los cambios sin un modelo centralizado.

Conozco MVVMFoundation y el trabajo de Tom Ershamam sobre WPF Commands Everywhere. Me llamó anticuado, pero creo que para comprender realmente un patrón, debes construir una aplicación que lo use desde cero. Que es lo que estoy haciendo, pero todo el tiempo sigo pensando que debo estar perdiendo algo esencial porque no parezco ser capaz de callar esta pequeña voz en mi cabeza que me sigue diciendo que debe haber una mejor manera.

+0

¡Tienes tanto razón! – Faruz

+0

¿Quizás podría formular sus quejas/dudas en una pregunta? –

+0

La pregunta sería ¿me estoy perdiendo algo con respecto a MVVM o hay una mejor manera? –

Respuesta

3

Cualquiera que sea el marco/Arquitectura/patrón, que siempre tendrá algo que responde a un clic de botón, en una barra de herramientas/menú o forma normal. Y necesita algo que diga si el botón/menú debe estar habilitado. Entonces, la interfaz de ICommand es buena para esto. Estoy de acuerdo con Petoj, no necesitas una nueva clase. Escribí una implementación simple que requiere 1 o 2 delegados, uno para la respuesta real al clic (el método Execute) y uno opcional para el estado "habilitado" del comando. De esta forma, ViewModel no se satura.

Pero acepto que este no es un repositorio centralizado de comandos. ¿Pero realmente quieres uno?Prefiero que los comandos específicos de una parte de una aplicación estén en el modelo de vista correspondiente, con los eventos apropiados generados cuando se debe notificar al resto de la aplicación.

Para el cuadro de lista, vinculo la propiedad SelectedItem a una propiedad en ViewModel. Con INotifyPropertyChanged, cualquier parte de tu código puede reaccionar al cambio.

La comunicación entre ViewModels es una buena pregunta. Si necesita vistas diferentes en la misma pantalla, puede tener un modelo de vista "super" que contenga el modelo de vista de cada vista. Existen bastantes frameworks MVVM. Utilicé partes de Mark Smith's MVVM helpers, que es bastante liviana y útil.

3

bien escribiendo un nuevo comando que impelements ICommand parece un poco más de matar echar un vistazo a esta clase: VB.NET: Public Class RelayCommand Implementa ICommand

#Region " Declarations" 
    Private mCanExecute As Predicate(Of Object) 
    Private mExecute As Action(Of Object) 
#End Region 

#Region " Constructors" 
    Public Sub New(ByVal canExecute As Predicate(Of Object), ByVal execute As Action(Of Object)) 
     mCanExecute = canExecute 
     mExecute = execute 
    End Sub 

    Public Sub New(ByVal execute As Action(Of Object)) 
     mCanExecute = Nothing 
     mExecute = execute 
    End Sub 
#End Region 

#Region " Events" 
    Public Custom Event CanExecuteChanged As EventHandler Implements System.Windows.Input.ICommand.CanExecuteChanged 
     AddHandler(ByVal value As EventHandler) 
      AddHandler CommandManager.RequerySuggested, value 
     End AddHandler 
     RemoveHandler(ByVal value As EventHandler) 
      RemoveHandler CommandManager.RequerySuggested, value 
     End RemoveHandler 
     RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs) 
      Throw New ApplicationException("Can't raise custom command!") 
     End RaiseEvent 
    End Event 
#End Region 

#Region " Public Methods" 
    Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute 
     If (mCanExecute Is Nothing) Then 
      Return True 
     End If 
     Return mCanExecute(parameter) 
    End Function 

    Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute 
     mExecute(parameter) 
    End Sub 
#End Region 

End Class 

C#

public class RelayCommand : ICommand 
{ 

    #region Declarations 
    private Predicate<object> mCanExecute; 
    private Action<object> mExecute; 
    #endregion 

    #region Constructors 
    public RelayCommand(Predicate<object> canExecute, Action<object> execute) 
    { 
     mCanExecute = canExecute; 
     mExecute = execute; 
    } 

    public RelayCommand(Action<object> execute) 
    { 
     mCanExecute = null; 
     mExecute = execute; 
    } 
    #endregion 

    #region Events 
    public event EventHandler CanExecuteChanged { 
     add { 
      CommandManager.RequerySuggested += value; 
     } 
     remove { 
      CommandManager.RequerySuggested -= value; 
     } 
    } 
    #endregion 

    #region Public Methods 
    public bool CanExecute(object parameter) 
    { 
     if ((mCanExecute == null)) { 
      return true; 
     } 
     return mCanExecute(parameter); 
    } 

    public void Execute(object parameter) 
    { 
     mExecute(parameter); 
    } 
    #endregion 

} 

y para usarlo solo expone una propiedad de tipo ICommand que devuelve un nuevo RelayCommand con delegados a una función ...

vb.net

Private mDeleteCommand As ICommand 

Public ReadOnly Property DeleteCommand() As ICommand 
    Get 
     If (mDeleteCommand Is Nothing) Then 
      mDeleteCommand = New RelayCommand(AddressOf CanDeleteTodo, AddressOf DeleteTodo) 
     End If 
     Return mDeleteCommand 
    End Get 
End Property 

C#

private ICommand mDeleteCommand; 
public ICommand DeleteCommand { 
    get { 
     if ((mDeleteCommand == null)) { 
      mDeleteCommand = new RelayCommand(CanDeleteTodo, DeleteTodo); 
     } 
     return mDeleteCommand; 
    } 
} 
+0

Muchas gracias, vi este código en el excelente artículo de Josh Smith, y de hecho es útil, pero aún deja la pregunta abierta con respecto a por qué tenemos que usar comandos siempre? ¿Los eventos en el código son intrínsecamente malvados o hay algunas circunstancias cuando están justificados? –

+0

estoy lejos de ser un experto pero trato de mantener mi código de vista atrás vacío si puedo, así que, hasta donde yo sé, no se puede enlazar a un evento para que te deje con comandos ... (y debo decir que tienes una vista XAML pura es agradable, solo tiene que mirar en un solo lugar) – Peter

+0

El código detrás no es ViewModel. La forma más fácil de obtener la vista para ejecutar el código en ViewModel es vincular un comando al objeto GUI. Los manejadores de eventos en el código subyacente pueden llamar al ViewModel, pero si usa el enlace se corta el intermediario. –

0

Ok, así que para darle a este hilo algún tipo de cierre para referencia futura. Primero, muchas gracias por las respuestas. El RelayCommand es realmente una buena idea; realmente optimiza mucho las cosas y hace que sea fácil probar y trabajar. Parece que es el camino a seguir. La vinculación al elemento seleccionado también parece resolver el problema relacionado con la falta de soporte de comando en ItemsControl. En cuanto a la comunicación entre ViewModels, no estoy convencido de que tener un modelo de súper vista resuelva mi problema, ya que vincula el modelo a mi árbol visual. Además, no he encontrado una forma en esta estructura de tener una forma limpia, independiente del objeto para comunicarse entre todos los modelos de vista en diferentes jerarquías. Entonces lo que estoy probando es crear primero un modelo centralizado que sea un singleton que implemente la interfaz INotifyPropertyChanged. Los ViewModels pueden tener una instancia de este modelo y actuar sobre la propagación de los correspondientes cambios de propiedad utilizando nuestro patrón Old Good the Observer. Parece que funciona bien, aunque estoy un poco preocupado por las referencias circulares. ¿Qué piensas?

1

Helo Julio,

tener este aspecto es una entrada antigua, pero que realmente me gusta su pregunta.

Recientemente soy un programador de flex y un WPF también. Sabía que Cairngorm (permite decir [C]) framework bien, aprendí a usar Presentation model usando Parsley Framework, y en WPF me doy cuenta de que Presentation Model se ha cambiado a un patrón MVVM.

Comando

comando en [C] es diffrent que en MVVM, comando en [C] es más satisfechos como Command Pattern, donde en [C] acto controlador como Invoker, así que en [C] el comando realmente se puede hacer para admitir Cadena, Deshacer, Transacción, etc. En el Comando MVVM está lejos del patrón de Comando. La idea principal de usar Command en MVVM es que ICommand es la única forma de enlazar con la operación. En Flex, enlaza fácilmente el método al evento usando click="{viewmodel.doOperation()}" pero no en WPF.

Localizador Modelo

Es una mala práctica para centralizar el estado de la aplicación en un único localizador modelo como [C] lo hizo. Por otro lado, perderás la oportunidad de probar tu código "fácilmente" si lo haces. Cuanto más dependiente sea tu código, más difícil será tu prueba, si tu localizador de modelos contiene toneladas de modelos más pequeños, entonces ya dependes mucho de tu código. Y, lo más aterrador de usar singleton es que es imposible burlarse. por lo tanto, si su localizador modelo no es amigable para pruebas de unidades, su proceso de prueba unitario puede estar lleno de dolor.

En realidad, no hay una mejor práctica utilizada en el contexto de MVVM para el modelo compartido entre las vistas como usted menciona, pero debe echarle un vistazo al término dependency injection para lograr eso.

Saludos

+0

Tiene razón acerca de la capacidad de prueba de un modelo centralizado, pero sigo creyendo que para compartir el estado de ciertos elementos globales entre los modelos de vista, un modelo central es mucho más simple de desarrollar. Se llega a un equilibrio entre difícil de codificar (= mayor probabilidad de errores) y facilidad de prueba. –

+0

sí, estoy de acuerdo, en algunos casos es aceptable, pero para programas de software de buena calidad en algún momento debemos seguir algunas buenas prácticas, puede consultarlo aquí http://misko.hevery.com/2008/08/17/singletons-are -patológicos-mentirosos /. El enlace que brindo discute sobre singleton, pero en realidad se menciona acerca de GLOBAL STATE que no era una buena práctica en programación. Marque que el autor del enlace que proporciono es un ** Coach ágil ** del desarrollador de Google. – ktutnik

0

Las personas que discuten sobre MVVM han hecho que parezca demasiado teórico y Flex tiene realmente una arquitectura diferente todos juntos que hace MVVM poco complicado para las personas que vienen de flexión.

Déjeme darle ejemplo muy sencillo,

En Flex, sobre todo que crear uno MXML para la interfaz de usuario de componentes, y tenemos nuestro modelo enlazable y sobre todo escribir nuestro código en los eventos de interfaz de usuario de componentes, así como no Componentes de UI. Por ejemplo, WebService, HttpService, etc., no son componentes UI, pero aún pueden estar dentro del MXML y se puede acceder fácilmente dentro del código MXML.

Así que, básicamente, puede tener, Model + View + Controller en un archivo MXML organizado muy fácilmente.

En Silverlight, en XAML, solo puede tener elementos de la interfaz de usuario como elementos secundarios del control de página/usuario que está modificando. Hay limitaciones, XAML le permite poner su elemento no UI solo en recursos, y la variable tipeada del recurso que ha agregado no es fácilmente accesible dentro del código detrás de XAML, tiene que llamar a find resource para acceder al código.

Para facilitar las cosas, MVVM obliga a definir diferentes archivos.

Usted tiene un archivo, que es su propio modelo, por ejemplo Customer.cs

Usted tiene otro archivo, que es su modelo de vista, que básicamente es una combinación de Modelo + Comandos. Y escribe tu código de Controlador en el evento Ejecutado del Comando.

Tiene otro archivo, que es su Vista, la vista básicamente se une a todas las propiedades de ViewModel que son Model o Commands.