web-dev-qa-db-fra.com

prisme vs mvvm lumière pour wpf

Nous démarrons un projet WPF avec MVVM et devons choisir PRISM ou MVVM Light (je suis nouveau dans ces deux frameworks). J'ai lu quelques articles mais j'ai encore quelques questions. Quelqu'un peut-il nous éclairer sur les aspects suivants avec les deux cadres ?:

  1. Performance: l'un ou l'autre des frameworks fonctionnera-t-il mieux que l'autre pour une raison quelconque?

  2. Communication au sein de l'application (viewmodel à viewmodel ou entre modules, etc.): J'ai lu que MVVM Light a Messenging Service qui semble également assez facile. Mais PRISM ne semble pas avoir d'équivalent. Est-ce vrai? Comment PRISM gérerait-il les interactions?

  3. Tests unitaires: J'ai lu que PRISM prend mieux en charge les tests unitaires. Peut-on encore écrire des tests NUNIT ou VSTS dans MVVM Light également?

28
Padmaja
  1. Je viens de déplacer un projet de Prism vers MvvmLight et il semble fonctionner plus rapidement (très subjectif).

  2. Prism et MvvmLight ont tous deux une réalisation Mediator (IEventAggregator in Prism, IMessenger in MvvmLight). Mais IMessenger a plus de capacités (par exemple, l'envoi de messages avec des jetons) par rapport à IEventAggregator et est beaucoup plus pratique à utiliser (voir l'élément suivant).

    MvvmLight possède également une classe ViewModelBase plus puissante.

  3. Les applications qui utilisent MvvmLight sont beaucoup plus faciles à tester que celles qui utilisent Prism. Par exemple, IMessenger est plus facile à moquer qu'IEventAggregator.

PrismViewModel.cs

using System;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.ViewModel;

// An ugly empty event class
public class StringEvent : CompositePresentationEvent<string> { }

public sealed class PrismViewModel : NotificationObject
{
    private readonly IEventAggregator _eventAggregator;

    private string _name;

    public PrismViewModel(IEventAggregator eventAggregator)
    {
        if (eventAggregator == null)
            throw new ArgumentNullException("eventAggregator");

        _eventAggregator = eventAggregator;
        _eventAggregator.GetEvent<StringEvent>().Subscribe(s => Name = s);
    }

    public string Name
    {
        get { return _name; }
        set
        {
            // boiler-plate code
            if (value == _name) 
                return;
            _name = value;
            RaisePropertyChanged(() => Name);
        }
    }

    public void SendMessage(string message)
    {
        _eventAggregator.GetEvent<StringEvent>().Publish(message);
    }
}

PrismViewModelTestCase.cs

using System;
using FluentAssertions;
using Microsoft.Practices.Prism.Events;
using NSubstitute;
using NUnit.Framework;

public class PrismViewModelTestCase
{
    private static PrismViewModel CreateViewModel(IEventAggregator eventAggregator = null)
    {
        // You can't return Substitute.For<IEventAggregator>()
        // because it returns null when PrismViewModel's constructor
        // invokes GetEvent<StringEvent>() method which leads to NullReferenceException
        return new PrismViewModel(eventAggregator ?? CreateEventAggregatorStub());
    }

    private static IEventAggregator CreateEventAggregatorStub()
    {
        var eventAggregatorStub = Substitute.For<IEventAggregator>();
        eventAggregatorStub.GetEvent<StringEvent>().Returns(Substitute.For<StringEvent>());
        return eventAggregatorStub;
    }

    [Test]
    public void Constructor_WithNonNullEventAggregator_ExpectedSubscribesToStringEvent()
    {
        // Arrange
        var stringEventMock = Substitute.For<StringEvent>();

        var eventAggregatorStub = Substitute.For<IEventAggregator>();
        eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);

        // Act
        CreateViewModel(eventAggregatorStub);

        // Assert
        // With constrained isolation framework you can only mock virtual members
        // CompositePresentationEvent<TPayload> has only one virtual Subscribe overload with four parameters
        stringEventMock.Received()
                       .Subscribe(Arg.Any<Action<string>>(), Arg.Any<ThreadOption>(), Arg.Any<bool>(),
                                  Arg.Any<Predicate<string>>());
    }

    [Test]
    public void Name_ExpectedRaisesPropertyChanged()
    {
        var sut = CreateViewModel();
        sut.MonitorEvents();

        sut.Name = "any-value";

        sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
    }

    [Test]
    public void SendMessage_ExpectedPublishesStringEventThroughEventAggregator()
    {
        // Arrange
        var stringEventMock = Substitute.For<StringEvent>();

        var eventAggregatorStub = Substitute.For<IEventAggregator>();
        eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);

        var sut = CreateViewModel(eventAggregatorStub);
        const string expectedPayload = "any-string-payload";

        // Act
        sut.SendMessage(expectedPayload);

        // Assert
        stringEventMock.Received().Publish(expectedPayload);
    }
}

MvvmLightViewModel.cs

using System;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;

public sealed class MvvmLightViewModel : ViewModelBase
{
    private string _name;

    public MvvmLightViewModel(IMessenger messenger)
    {
        if (messenger == null)
            throw new ArgumentNullException("messenger");

        // ViewModelBase already have field for IMessenger
        MessengerInstance = messenger; 
        MessengerInstance.Register<string>(this, s => Name = s);
    }

    public string Name
    {
        get { return _name; }
        set { Set(() => Name, ref _name, value); // Chic!  }
    }

    public void SendMessage(string message)
    {
        MessengerInstance.Send(message);
    }
}

MvvmLightViewModelTestCase.cs

using System;
using FluentAssertions;
using GalaSoft.MvvmLight.Messaging;
using NSubstitute;
using NUnit.Framework;

public class MvvmLightViewModelTestCase
{
    private static MvvmLightViewModel CreateViewModel(IMessenger messenger = null)
    {
        return new MvvmLightViewModel(messenger ?? Substitute.For<IMessenger>());
    }

    [Test]
    public void Constructor_WithNonNullMessenger_ExpectedRegistersToStringMessage()
    {
        var messengerStub = Substitute.For<IMessenger>();

        var sut = CreateViewModel(messengerStub);

        messengerStub.Received().Register(sut, Arg.Any<Action<string>>());
    }

    [Test]
    public void Name_ExpectedRaisesPropertyChanged()
    {
        var sut = CreateViewModel();
        sut.MonitorEvents();

        sut.Name = "any-value";

        sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
    }

    [Test]
    public void SendMessage_ExpectedSendsStringMessageThroughMessenger()
    {
        var messengerMock = Substitute.For<IMessenger>();
        var sut = CreateViewModel(messengerMock);
        const string expectedMessage = "message";

        sut.SendMessage(expectedMessage);

        messengerMock.Received().Send(expectedMessage);
    }
}

Inconvénients du prisme:

  • c'est un projet non entièrement open-source (le dépôt officiel de Prism est en lecture seule)
  • il ne s'est plus développé activement
  • l'utilisation directe de ses classes conduit à une plaque de chaudière et à un code moins lisible

Je pense que tout nouveau projet devrait être basé sur des solutions et des approches modernes. À mon humble avis, tout framework MVVM moderne (comme Catel, Caliburn.Micro, MvvmLight, ReactiveUI) est bien meilleur que Prism.

20
Vladimir Almaev

Vous ne pouvez pas comparer complètement Prism et MvvmLight.

Prism concerne davantage l'architecture d'application, même si Prism est connu sous le nom de framework MVVM. En fait, jusqu'à Prism 5, cela n'avait rien à voir avec MVVM et il n'avait pas de classe BaseViewModel dans Prism 4.1 et dans les versions antérieures.

Prism n'est pas un framework MVVM, c'est un framework d'application, il se situe plus haut que cela. Prism 5 a introduit un certain support pour MVVM et Prism 6 est allé plus loin.

MVVM n'est qu'un autre aspect des problèmes que le prisme fournit des conseils pour résoudre.

C'est comme comparer Angular vs Knockout. AngularJS gère l'ensemble de l'application et définit des directives sur la façon dont le code de l'application doit être structuré, alors qu'avec KnockoutJS la structure de l'application dépend entièrement de vous. C'est un cas similaire entre Prism et MvvmLight.

Prism fournit une implémentation d'une collection de modèles de conception qui sont utiles pour écrire des applications XAML bien structurées et maintenables, y compris MVVM, injection de dépendance, commande, agrégation d'événements, etc. La fonctionnalité principale de Prism est une base de code partagée dans une bibliothèque de classes portable ciblant ces plates-formes; WPF, Windows 10 UWP et Xamarin Forms.

Je recommanderais d'utiliser Prism si vous développez une application de niveau entreprise utilisant wpf.

Veuillez regarder ce webinaire sur MVVM simplifié avec Prism. Le présentateur est Brian Lagunas: https://www.youtube.com/watch?v=ZfBy2nfykqY

Je ne crois pas que MS ait jamais promu PRISM en tant que "cadre" au même sens que MVVM Light, Caliburn, etc. sont "annoncés". Ma compréhension est que le PRISM a toujours été présenté au "monde" comme une "pratique". Ce qui, je crois, signifie simplement une manière organisée de créer des applications. Cela devient un peu déroutant à cause de tout le code fourni avec PRISM et on peut le considérer comme un "framework" qui peut être utilisé pour construire des applications. Et, c'est vrai, vous pouvez utiliser le code fourni avec PRISM pour créer vos propres applications. Mais, PRISM, dans mon esprit est beaucoup plus compliqué que les "frameworks" disponibles pour MVVM et il y a une courbe d'apprentissage abrupte ainsi que la possibilité de "surpasser" et de rendre votre application plus complexe que nécessaire. Si vous avez le temps d'apprendre le dernier "framework" ou la "pratique" au moment où vous créez votre application, c'est bien! D'après mon expérience, je n'ai pas le luxe de prendre en compte l'apprentissage d'une nouvelle "pratique" ou du dernier "cadre", mais je dois faire le travail. Dans un monde idéal, on aimerait pouvoir utiliser le "cadre" le plus récent et le plus efficace ou utiliser les "pratiques" les plus récentes, mais il suffit parfois de s'en tenir à ce que vous savez déjà et de le faire.

4
John Blacker