Are current MVVM view model practices a violation of the Single Responsibility Principle?

穿精又带淫゛_ 提交于 2019-12-02 19:09:30

Single Responsibility as Martin defines it:

"THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE."

A ViewModel, as far as MVVM is concerned is really just a specialized implementation of a Presentation Model.

So while it could be argued that a Presentation Model should only represent the state of the UI, and that a Presenter/Controller should always broker the commands between the UI and the Presentation Model. If one follows this idea, with SRP dividing on State and Commands, then adding a command should not affect the class that represents state. Therefore MVVM would break SRP.

However...

I think this is grasping at straws. MVVM is a fairly specialized implementation used basically in WPF/Silverlight (and now browser clients).

Patterns are designed to make designs simpler where the alternative would be more cumbersome or less maintainable. Since MVVM is designed to take advantage of the extremely rich data binding capabilities of the presentation technologies, then it is a worthwhile trade off.

No! MVVM does not violate SRP, (the programmer does, lol!)

There is no reason that using the MVVM pattern needs to ignore the SRP. MVVM does not mean that there is only one Model Class, one View-Model Class, and one View Class. Certainly, if you only had one View Class, you could only ever show one simple screen.

Those classes that are in the View tier, should be responsible for one thing; doing, deciding, or containing. A View can consist of several sub-views who's jobs are to do certain pieces of the users interractions. Consider a basic form, it has a display grid, items in the grid can be edited, and there is a "Save" button.

The main View would be a container for two other views; the datagrid (a user control, or something) and a command control. The datagrid then is responsible for choosing the right childview to render data in; in essense it's a container that databinds. The View to edit items is a child view of the datagrid, which is in-turn a child of the main View. Lastly the command control is a set of buttons (in this case a single one) who's single responsibility is to raise signals that commands have been issued by the user.

In this way the Edit View (used by the DataGrid) is agnostic about what uses it, and has one responsibility; Same with the command control. Likewise the DataGrid doesn't care about who uses it, only that it can contain the Edit View (child). Nice SRP there.

ViewModels to match the Views (and children) are also responsible for one thing. The Edit View Model is a container to which the Edit View Binds; it simply contains the data fields that can be displayed/edited. It doesn't care about anything but signalling when one of its properties change. The Command Button View Model is a class that does things. It's commands are bound to the buttons, and it will do work based on what the user clicks on. It will have to have access to other parts of the ViewModel(s) to do it's work.

The main page View Model is there to contain the other child views. It's sole responsibility is as an initializer, making all the required ViewModel instances, and passing constructor parameters to other ViewModel instances (say, the Command Button View Model so it knows where to get data for it's work)

It's natural to cram a whole bunch of functionality into a single ViewModel that a large View would bind to. But it doesn't have to be that way, and SRP can be maintained in MVVM.

The main goal of MVVM is to allow for testable design, the Model layer can be tested independantly, all classes in the Model can easily follow SRP. The ViewModel can be tested without the need of a view; it gets trickier to think SRP in the ViewModel, but it is certainly doable; just remember to break out your classes so they only have one concern. The View is bound to parter ViewModels, with any luck, your testing of the ViewModel makes snapping the View(s) on super easy. Remember you can have each View-let adhere to SRP to be part of a larger conglomerate View (container).

TL;DR? To answer your question directly, the View is a collection of classes that does not break the SRP. Thus, when the ViewModel is abstracted from the View(s) (View-First), they are also a collection of classes that adhere to good SRP.

I consider many of the current practices around MVVM violate SPR (at least). This is yet another situation where simply adding controllers back to MVVM would solve all the problems cleanly. I call it MVCVM :)

The pattern we are using successfully on all recent projects is to register controllers only, in modules, and initialise them at startup. The controllers are very light/slim and the only thing that needs to hang around for the life of the app listening for, or sending, messages. In their initialise methods they then register anything they need to own (views and viewmodels etc). This lightweight logic-only-in-memory pattern makes for slimmer apps too (e.g. better for WP7).

The problem with just using VMs, as you have found, is that eventually you hit cases where they need to know about views, or commands, or other stuff no self-respecting ViewModel should be involved with!

The basic rules we follow are:

  • Controllers make decisions based on events
  • Controllers fetch data and place it in appropriate View Model properties
  • Controllers set ICommand properties of View Models to intercept events
  • Controllers make views appear (if not implied elsewhere)
  • View Models are "dumb". The hold data for binding and nothing else
  • Views know they display a certain shape of data, but have no idea where it comes from

The last two points are the ones you should never break or separation of concerns goes out the window.

Simply adding controllers back into the MVVM mix seems to solve all the problems we have found. MVVM is a good thing, but why did they not include controllers? (but this is of course just my opinion) :)

What it boils down is that the view model is a composite of the following:

  • an abstraction of the view
  • commands
  • value converters
  • view state

I don't see why you've separated the first two items. Commands are part of the view.

As for the rest - you're right. In some cases. I've built applications where the tasks of value conversion and maintaining view state were sufficiently complex that it didn't make sense for a single view model class to do it all, and I broke them out into separate classes that interoperate with the VMs.

So?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!