可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've always been a little confused as to what a "model" should or shouldn't contain - tutorials and examples often contradict each other. So far I've been playing it safe, with my models exposing nothing more than "UI stuff", e.g. properties for binding to the view, plus validation logic. But is it acceptable to have other business logic in a model?
Let's say I want to control a mechanical pump via a web service, which provides methods to turn the pump on at a given speed, and to turn it off again. My UI view might include "on" & "off" buttons, plus a textbox to set the speed. With this in mind, my model might start out like this:-
public class Pump
{
public int Speed { get; set; }
}
// The real thing would implement INotifyPropertyChanged, validation, etc.
Is this all the model should be doing, or is it acceptable to expose the business logic for turning the pump on and off - either as methods for the view-model to call, or maybe even as ICommands that can be bound directly to the view's buttons? Or should all this be in the view-model?
Edit
Not sure why the downvotes, as I think this was a perfectly reasonable question. While there are many MVVM tutorials and examples on the web, they often provide conflicting advice as to what goes where, much like the answers provided below. I even ended up reading the "Advanced MVVM" e-book by WPF expert Josh Smith - the book didn't mention models whatsoever!
Anyway, I found a good resource here for anyone who wants to undersatnd the structure of MVVM. While the link takes you into documentation for Microsoft's Prism framework, this particular page is all about MVVM structure, with little or no Prism-specifics. I found the page to be very informative and has at least put my mind at ease, confirming that what I've been doing over the last couple of years to be perfectly valid - that is, having models that implement INPC and validation (IDataErrorInfo), and binding these models directly to the view via VM properties (rather than duplicate model properties in the VM, as some answers below advocate).
回答1:
Purpose of model is to model your domain. Entities, business logic, services - this all belongs to model. What binds it with UI is view model.
In your pump example, the Pump
class should not implement INotifyPropertyChanged
in order to utilize it in UI, because it should not be used in UI. That's the role of view model. More appropriate design would be
public class PumpService
{
public void Start() { ... };
public void Stop() { ... };
}
public class PumpControllerViewModel : INotifyPropertyChanged
{
public PumpControllerViewModel(PumpService service)
{
StartPump = new Command(() => service.Start());
// ...
}
public ICommand StartPump { get; private set; }
public ICommand StopPump { get; private set; }
}
Model is in most cases almost invisible to UI. View model often exposes model properties, but seldom does so directly. If your application would require to display pump speed view model would have to expose it as observable property.
Update in response to OP question
(...) what if I had a more complex model with many properties, e.g. a Customer? (...) You suggest exposing each model property via an observable property on the VM - doesn't this involve a lot of duplication of properties, mapping code, etc?
Model complexity is irrelevant here and yes, such approach involes some code duplication (to some extent though).
If you keep your model simple (DTO/POCO-simple), then all you need to do is to rewrite POCO-model to INPC-view model. Model classes would have very little code while VM would handle to-the-view notifications (as to its role).
Assuming that most complex models are created via factory method/builder/service (as in, not directly), so could be the view models. And then you can utilize automappers, serializers or whatever that is you need to ease the duplication process.
You will have to maintain two similar classes but this is a worthy tradeoff for a clean separation MVVM offers.
回答2:
But is it acceptable to have other business logic in a model?
Yes it is and domain-driven advocates would encourage this design. Your business logic would be here, but not persistence logic.
Or should all this be in the view-model?
You can use your view-models as DTOs - classes with only properties to display on the view. They should have no or very little business logic - this is a mistake that many devs make. Your view will be bound to your view-model and the view-model will interact with your model, setting properties on it and invoking the logic it contains.
回答3:
Well, actually... the "UI stuff" you mention should go in the ViewModels
. Some of that stuff could be in the models also, and validation could be directly in the views (plus bindings, etc.).
I recommend reading the Prism 4.1 Developer's Guide (chapter 5), right in the first section.
回答4:
Interestingly in this case I would tend to use the WebService
as the Model
.
I think you also may have confused your Model
with the ViewModel
. Normally the UI specific stuff is definitely on the ViewModel
leaving the Model
clear to do the actual work. For example
View
- Has a slider to set the pump speed, and a few butttons, Fast, Slow, Stop. These buttons would be bound to relevant commands on the ViewModel
(this really helps testing too), and the slider is bound to the Speed
property of the ViewModel
.
public class ViewModel
{
public ICommand StopCommand {get; private set;}
// other commands
public ViewModel()
{
StopCommand = new MyCommand( ()=> { Speed = 0; });
// other commands
}
public int Speed
{
//Using webservice as model
get { return WebService.GetSpeed(); }
set { WebService.SetSpeed(value); }
}
}
There is a really good exmaple post of this by Laurent Buyginon creator of MVVMLight, but I can't find it. Its somewhere in http://blog.galasoft.ch/posts/