From the translator :
Many materials on MVC and its derived patterns have already been published, but everyone understands them in their own way. On this basis, differences and holivars arise. Even experienced developers argue about what is the difference between MVP, MVVM and Presentation Model and what one or another component should do in each pattern. The situation is aggravated by the fact that many do not know the true role of the controller in the classic version of MVC. I bring to your attention the translation of a good review article , which clarifies a lot and puts everything in its place .
Before we begin to dive into the details of the Model-View-ViewModel pattern (MVVM), I think it would be useful to describe the similarities and differences between MVVM and other design patterns for separating the model and the view (MV * patterns).
There are quite a few MV * patterns: Model-View-Controller, Model-View-Presenter, Presentation Model, Passive View, Supervising Controller, Model-View-ViewModel and many others:
Looking at the diagrams, you certainly see that the arrows show the relationship between the components. But is this the only difference? Is the Controller the same as Presenter or PresentationModel? How would you compare the Model-View-Presenter and Model-View-ViewModel? In this article, I'm going to describe the similarities and differences between the most common MV * patterns.
How would you build a user interface (UI) without using the above patterns? Would take the form, add widgets to it, and write the logic in the code. This code, which describes the logic of View, is tightly connected to the user interface, as it directly interacts with the elements on the screen. This is a good, but straightforward approach. It is applicable only for very simple interfaces. When logic becomes more complex, supporting such a UI can turn into a nightmare!
The root of the problem lies in the fact that building a UI in this way violates the single responsibility principle , which says: " A class must have only one reason for change ." If a UI component contains code for display, logic, and data, then it has several reasons for changing. For example, if you want to change the type of user element that is used to display data, then the changes should not affect the logic. However, since logic is so closely connected with controls, it will also have to be changed. This is the so-called “code smell” ( code smell ), which signals that the principle of sole responsibility has been violated.
Thus, if the form contains code for displaying controls, interface logic (what happens when a button is pressed), and data to display on the screen, you will encounter the following problems:
Complication of support
Changes in UI, logic, or data are likely to result in changes in other parts. Therefore, it is much more difficult to make edits, which makes support difficult.
Deterioration in testability
The logic and data of the application can be written in such a way that each component can be tested separately. However, the code associated with the user interface is difficult to unit test, because it often requires user input to run the logic in the UI. In addition, any visualization often requires an assessment by the person that everything “looks right.” Note that there are solutions for automating user interface testing. However, they only mimic user interaction. As a rule, they are more difficult to configure and maintain than unit tests, and are most often used for integration testing, since this requires the launch of the entire application .
Although each of the patterns has quite a few differences, their goals are similar: to separate the UI code (View) from the logic code (Presenter, Controller, ViewModel, etc.) and the data processing code (Model). This allows each of them to develop independently. For example, you can change the appearance and style of an application without affecting logic and data.
In addition, since the logic and data are separate from the display, they can be tested separately. For simple applications this may not be so important. For example, if your application is a simple data editor. However, if you have more complicated interface logic, then the ability to automatically verify that it works correctly will be very valuable.
One of the very first patterns for separating the presentation from logic and model was the Model-View-Controller (MVC). This concept was described by Trygve Reenskaug .
In 1979! (I was not even born then).
This pattern was designed to write Smalltalk applications. But in those days, programming was not the same as today. There was no windows. There was no graphical user interface. There were no widget libraries. If you want a user interface, you need to draw it yourself. Or if you want to interact with input devices, such as a keyboard.
But what Trughwe did was quite revolutionary. Where everyone mixed up the mapping code, logic, and data, he applied a pattern to divide these responsibilities between the individual classes.
The problem with the MVC pattern is that it is probably one of the most misunderstood patterns in the world. And I think it's because of the name. Trygve first called the Model Model-View-Editor, but later settled on Model-View-Controller. It is clear what Model is (data) and what is View (what I see on the screen). But what is a controller? Is the application controller the same as in the MVC pattern? ( No, but you can see where the confusion came from ).
What are these Model, View and Controller:
Model
The model is the data of your application, the logic of their receipt and saving. This is often a domain model based on a database or on the results from web services. In some cases, the domain model is well projected onto what you see on the screen. But sometimes it is necessary to adapt, modify or expand it before use.
View
View was responsible for displaying UI on the screen. Without widget libraries, this meant drawing blocks, buttons, input fields, etc., on its own. View can also monitor the model and display data from it.
Note from the translator:
It should be noted that the Controller receives input events directly, and not through the View. The controller interprets user input from the keyboard or mouse, and sends commands to the model and / or view to make the appropriate changes.
I wrote a quick example to illustrate what the controller would look like in a pure MVC implementation. I implemented it on the usual asp.net (not asp.net MVC), but without using any user controls. So this is a more traditional asp style. ( Yes, this is not a very good example, but I hope that it will be a starting point for understanding the true role of the controller ).
public class Controller { private readonly IView _view; public Controller(IView view) { _view = view; HttpRequest request = HttpContext.Current.Request; if (request.Form["ShowPerson"] == "1") { if (string.IsNullOrEmpty(request.Form["Id"])) { ShowError("The ID was missing"); return; } ShowPerson(Convert.ToInt32(request.Form["Id"])); } } private void ShowError(string s) { _view.ShowError(s); } private void ShowPerson(int Id) { var model = new Repository().GetModel(Id); _view.ShowPerson(model); } }
After many years, the programming paradigm has changed somewhat - user controls ( widgets ) have appeared. Widgets both draw themselves and interpret user input. The button knows what to do if you click on it. The input field knows what to do if you enter text in it. This reduces the need for a controller, and the MVC pattern has become less relevant. However, since there is still a need to separate application logic from presentation and data, another pattern called Model-View-Presenter (MVP) gained popularity.
Most examples of the MVC pattern focus on very small components, such as a text box implementation or a button implementation. When using more modern user interface technologies (Visual Basic 3 is modern compared to Smalltalk 1979), as a rule, there is no need for this pattern. But it can help if you are developing your widget using a very low level API (for example, Direct X).
The last couple of years, the MVC pattern has become relevant again, but for a different reason, due to the advent of ASP.NET MVC . The ASP.NET MVC framework does not use the concept of widgets, unlike ASP.NET. The ASP.NET MVC View is an ASPX control that renders HTML. And the controller processes the user's actions again, as it accepts HTTP requests. Based on the http request, it determines what to do (update the Model or display a specific View).
With the development of a visual programming environment and the introduction of widgets that encapsulate the rendering and processing of user input, there is no need to create a separate controller class. But developers still need to separate logic from presentation, only now at a higher level of abstraction. Because it turned out that if you create a form from several user elements, it also contains the interface and data logic. The MVP pattern describes how to separate the UI from the interface logic (what happens when interacting with widgets) and from the data (what data to display on the screen).
Model
This is the data of your application, the logic of their receipt and storage. Often it is based on a database or on the results of web services. In some cases, you will need to adapt, modify or expand it before using it in View.
View
Usually is a form with widgets. The user can interact with its elements, but when some event of the widget will affect the logic of the interface, View will send it to the presenter.
It is worth noting one important thing that the presenter does not communicate with the presentation directly. Instead, it communicates through the interface. Thanks to this, the presenter and the model can be tested separately.
There are two variants of this pattern: Passive View and Supervising Controller.
In this MVP view, the view knows nothing about the model, but instead provides simple properties for all the information that needs to be displayed on the screen. The presenter will read information from the model and update the properties in the View.
This would be an example of PassiveView:
public PersonalDataView : UserControl, IPersonalDataView { TextBox _firstNameTextBox; public string FirstName { get { return _firstNameTextBox.Value; } set { _firstNameTextBox.Value = value; } } }
As you can see, you need to write quite a lot of code in both the View and the presenter. However, this will make the interaction between them more testable.
In this embodiment, the MVP view is aware of the model and is responsible for associating the data with the display. This makes the communication between the presenter and the View more concise, but at the expense of the testability of the View-Presenter interaction. Personally, I hate the fact that this pattern contains the name “Controller”. Because the controller is again not the one in MVC and not the same as the Application Controller.
This would be an example of representation in the Supervising Controller pattern:
public class PersonalDataView : UserControl, IPersonalDataView { protected TextBox _firstNameTextBox; public void SetPersonalData(PersonalData data) { _firstNameTextBox.Value = data.FirstName; } public void UpdatePersonalData(PersonalData data) { data.FirstName = _firstNameTextBox.Value; } }
As you can see, this interface is less detailed and places more responsibility on View.
Martin Fowler describes another approach on his website to achieve a shared responsibility called the Presentation Model . PresentationModel is a logical representation of the user interface, without relying on any visual elements.
PresentationModel has several responsibilities:
Contains user interface logic:
Like the presenter, PresentationModel contains user interface logic. When you click a button, this event is sent to PresentationModel, which then decides what to do with it.
Provides data from the model for display on the screen.
PresentationModel can convert data from a model so that it is easily displayed on the screen. Often the information contained in the model cannot be directly used on the screen. You may first need to convert the data, complete it, or collect it from several sources. This is most likely when you do not have full control over the model. For example, if you receive data from third-party web services or from an existing application database.
View can easily extract data from PresentationModel and get all the necessary information to display on the screen. One of the advantages of this approach is that you can create a logical and fully testable representation of your UI without relying on testing visual elements.
The Presentation Model pattern does not describe how the View uses data from the model (PresentationModel).
Finally, the Model-View-ViewModel pattern also known as MVVM or just the ViewModel template. It is very similar to the Presentation Model pattern:
In fact, the only difference is the obvious use of data binding capabilities ( databinding ) in WPF and Silverlight. Not surprisingly, because John Gossman was one of the first to mention this pattern on his blog.
ViewModel cannot communicate with View directly. Instead, it presents easily bound properties and methods as commands . View can bind to these properties to get information from the ViewModel and invoke commands on it (methods). This does not require View to know about the ViewModel. XAML Databinding uses reflection to bind View and ViewModel. Thus, you can use any ViewModel for View, which provides the desired properties.
Some of the things that I really like about this pattern when applied to Silverlight or WPF:
You get a fully tested logical model of your application.
Since the ViewModel provides the View with all the necessary information in a convenient way, the view itself can be quite simple. A designer can experiment with appearance and style in the Expression Blend editor and modify it without affecting the user interface.
I hope that this description of the most common MV * patterns helps you understand their differences.
Translator's comment:
Like the author, I hope that this description will help you understand the similarities and differences between MV * patterns. Having understood them, it will be easier for you to decide which of the patterns to apply in your application.
The main conclusions that can be drawn from the article:
- The model in all the patterns looks the same and has the same goal - receiving, processing, and saving data.
- In classic MVC, user input handles the Controller, not the View.
- Modern operating systems and widget libraries handle user input, so you no longer need the controller from the MVC pattern.
- The purpose of MV * -patterns: to separate from each other the display of UI, the logic of the interface and the data (their acquisition and processing).
- Using MV * -pattern in your application, you simplify its support and testing, separating data from the way they are visualized.
- MVP is quite a universal pattern and will work in many cases (this is my personal opinion). Which option to use: Passive View or Supervising Controller - you decide. Be guided by what you need: more control and testability or conciseness and brevity of the code. Maneuver between tasks and use one or the other approach.
- If the system has a good implementation of automatic data binding (databinding), then MVVM is your choice.
- Presentation Model is a good alternative to MVVM, and will be useful where there is no automatic linking. But you will have to write the binding code yourself (this is a simple but routine code). There are ideas on how to implement this elegantly, but we'll talk about this in the next article .
')
PS Separately, I want to thank my colleague Jeevuz for his help in preparing the translation .
Source: https://habr.com/ru/post/313538/
All Articles