Подтвердить что ты не робот

Призма против света mvvm для wpf

Мы запускаем WPF с проектом MVVM и должны принять решение о PRISM или MVVM Light (я новичок в обеих этих фреймворках). Я прочитал несколько сообщений, но все еще имею несколько вопросов. Может кто-то, пожалуйста, пролил свет на следующие аспекты w.r.t. обе рамки?:

  • Производительность: будет ли какая-либо одна структура работать лучше, чем другая, по какой-либо причине?

  • Связь в приложении (viewmodel to viewmodel или между модулями и т.д.): я прочитал, что MVVM Light имеет Messenging Service, который также выглядит довольно простым. Но PRISM, похоже, не имеет эквивалента. Это правда? Как PRISM обрабатывает взаимодействия?

  • Тестирование устройств: прочитайте, что PRISM лучше поддерживает Unit Testing. Можем ли мы еще написать тесты NUNIT или VSTS в MVVM Light?

4b9b3361

Ответ 1

  • Я просто перенес проект из Prism в MvvmLight и, похоже, работает быстрее (очень субъективно).

  • Оба Prism и MvvmLight имеют реализацию Медиатора (IEventAggregator в Prism, IMessenger в MvvmLight). Но у IMessenger больше возможностей (например, отправка сообщений с токенами) по сравнению с IEventAggregator и гораздо удобнее использовать (см. Следующий пункт).

    MvvmLight также имеет более мощный класс ViewModelBase.

  • Приложения, использующие MvvmLight, намного легче тестировать, чем те, которые используют Prism. Например, IMessenger легче высмеивать, чем 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 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);
    }
}

Недостатки Призмы:

  • не полностью открытый проект (официальный репозиторий Prism доступен только для чтения)
  • он больше не активно развивается
  • прямое использование его классов приводит к котельной плите и менее читаемому коду.

Я думаю, что любой новый проект должен основываться на современных решениях и подходах. IMHO, любой современный MVVM-каркас (например, Catel, Caliburn.Micro, MvvmLight, ReactiveUI) намного лучше, чем Призма.

Ответ 2

Вы не можете полностью сравнить Prism и MvvmLight.

Призма - это больше о архитектуре приложений, хотя Prism известен как MVVM. На самом деле до Prism 5 он не имел ничего общего с MVVM, и у него не было класса BaseViewModel в Prism 4.1 и ранее.

Призма не является основой MVVM, она представляет собой инфраструктуру приложения, которая находится выше. Призма 5 представила некоторую поддержку MVVM, а Prism 6 занял ее.

MVVM - это еще один аспект проблем, которые призмы обеспечивают руководство для решения.

Это похоже на сравнение Angular против нокаута. AngularJS управляет всем приложением и определяет рекомендации по структурированию кода приложения, тогда как с помощью KnockoutJS структура приложения полностью зависит от вас. Это аналогичный случай между Prism и MvvmLight.

Prism предоставляет реализацию набора шаблонов проектирования, которые полезны при написании хорошо структурированных и поддерживаемых приложений XAML, включая MVVM, инъекции зависимостей, команды, агрегации событий и т.д. Основная функциональность Prism - это общая база кода в Portable Class Library, предназначенная для этих платформ; WPF, Windows 10 UWP и Xamarin Forms.

Я бы рекомендовал использовать Prism, если вы разрабатываете приложение уровня предприятия, используя wpf.

Пожалуйста, смотрите этот веб-семинар о MVVM Made Simple с призмой. Ведущим является Брайан Лагунас: https://www.youtube.com/watch?v=ZfBy2nfykqY

Ответ 3

Я не считаю, что MS когда-либо продвигала PRISM в качестве "рамки" в том же смысле, что и MVVM Light, Caliburn и т.д. "рекламируются". Я понимаю, что PRISM всегда был представлен "миру" как "практика". Который, я считаю, просто означает организованный способ создания приложений. Это становится немного запутанным из-за всего кода, который поставляется с PRISM, и можно считать его "каркасом", который можно использовать для создания приложений. И, действительно, вы можете использовать код, поставляемый с PRISM, для создания собственных приложений. Но, PRISM, на мой взгляд, намного сложнее, чем "рамки" , доступные для MVVM, и есть крутая кривая обучения, а также возможность "перехитрить" и сделать ваше приложение более сложным, чем это необходимо. Если у вас есть время, чтобы узнать последние "рамки" или "практика" в то время, когда вы строите свое приложение, это БОЛЬШОЕ! Мой опыт в том, что у меня нет роскоши факторинга в изучении новой "практики" или последней "основы", но нужно выполнить эту работу. В идеальном мире вы хотели бы использовать новейшую и самую "основную" структуру или использовать новейшие "практики", но иногда вам просто нужно придерживаться того, что вы уже знаете, и сделать это.