简体   繁体   中英

How far to separate out business logic in MVVM

Here's a simplified ViewModel:

public class EditViewModel : BaseViewModel
{
    private Item _currentItem;
    public Item CurrentItem
    {
        get
        { return _currentItem; }
        set
        {
            if (_currentItem != value)
            {
                _currentItem = value;
                OnPropertyChanged("CurrentItem");
            }
        }
    }

    private ObservableCollection<Property> _itemProperties;
    public ObservableCollection<Property> ItemProperties
    {
        get { return _itemProperties; }
        set
        {
            _itemProperties = value;
            OnPropertyChanged("ItemProperties");
        }
    }

    public void AddProperty() //this is called from an ICommand
    {
        Property p = new Property{ ItemId = CurrentItem.ItemId };;
        CurrentItem.Properties.Add(p);
        ItemProperties.Add(p);
    }
}

What I'd like to do is to separate out the business logic here into a separate class. It keeps all the annoying MVVM boilerplate out of the way of the useful stuff, and in theory should lead to organizing the code into a more testable state.

We're starting to do this by creating separate "Logic" classes which inherit from BaseViewModel and then have the actual ViewModels inherit from their logic class. So:

public class EditLogic : BaseViewModel
{ }

public class EditViewModel : EditLogic
{ }

Then the logic goes in the logic class.

For some business logic this separation is simple - nice and clean. However, in the example I've given above I can't see a simple way of pulling that method out without a lot of unnecessary faff. Something like this (untested):

public class EditLogic : BaseViewModel
{
    public Property GetNewProperty(Item currentItem)
    {
        Property p = new Property{ ItemId = currentItem.ItemId };
        currentItem.Properties.Add(p); 
        return p;
    }
}

public class EditViewModel : BaseViewModel
{
    public void AddProperty() //this is called from an ICommand
    {
        ItemProperties(GetNewProperty(CurrentItem))
    }
}

This seems potentially confusing - since it's relying on CurrentItem implicitly being passed by reference - and unnecessarily convoluted to no great gain.

This is, of course, a very simple example which isn't worth fussing over. But it illustrates the point that in MVVM it's very easy to end up mixing your presentation/binding code with your business logic for the sake of convenience.

I could move some of the properties out from the EditViewModel to the EditLogic but then we're losing the advantages of separating these two out in the first place.

So: is it worth bothering with this at all? If so, how far should we pursue it? And are there any better methods for maintaining separation?

What you are looking for are services .

public interface IPropertyService
{
    Property GetNewProperty(Item currentItem);
}

You will of course need an implementation:

public class MyPropertyService : IPropertyService
{
    public Property GetNewProperty(Item currentItem)
    {
        //TODO
    }
}

You can then inject this service into the constructor of your view model as a dependency.

public class MyViewModel
{
    private IPropertyService _PropertyService;

    public MyViewModel(IPropertyService propertyService)
    {
        _PropertyService = propertyService;
    }

    public void AddProperty() //this is called from an ICommand
    {
        Property p = _PropertyService.GetProperty(CurrentItem);
        CurrentItem.Properties.Add(p);
        ItemProperties.Add(p);
    }
}

This will ensure that you don't need to create a myriad of view model base classes for your business logic. Instead, encapsulate your business logic in services and pass them into view models that depend on them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM