Writing in the front
Speaking of MVC, we are familiar with it. Counted up, SINCE it was first proposed in 1978, MVC has gone through more than 40 years. Over the course of those 40 + years, MVC has evolved into all sorts of branches, or variations — MVP, MVVM, of course.
In this article, I will introduce some representative evolutions of MVC and some variations of MVP.
The emergence and evolution of MVC architecture
1. Emergence of MV-Editor and MVC
MVC was first introduced by Trygve Reenskaug in the form of “Model-view-Editor” and was officially named “Model-View-Controller” a few months later.
In this version, as shown in Figure 1, Editor is treated as a special Controller, whose job it is to get a temporary object of a special Controller from the View when you need to modify the View, that is, Editor, The Model and View operations are done through the Editor.
2. Typical MVC Architecture (Model 1)
Later, there were many branches and evolutions of MVC, but all of them remained the same. The MVC structure at this stage is still roughly as shown in Figure 2 below, which is known as the “typical MVC structure.” (Let’s just call this phase Model 1 to distinguish it from the later Model 2.)
From the figure above, it is not difficult to find that the coupling between MVC pairs, especially the coupling between Model and View, will be significantly different from Model 2.
MVC with this architecture is relatively new, and most Android developers have been exposed to Model 2 architecture from the very beginning. Model 2 press the button first, and we’ll see later.
In this typical MVC structure, after user events occur in the View, the Controller will notify the Model for data storage and network requests, etc. The change of data in Model is to keep the display of UI in View synchronized with the data in Model by registering observers in the Model through the View.
Remember, there were no powerful GUI frameworks like Android, iOS, WP, Symbian OS, or even Web applications. At that time, desktop applications were the mainstay.
Let’s take Java desktop code as an example to help understand this typical MVC structure (code from “Android source code design Pattern analysis and Practice”).
Read/clear the image’s Model in Listing 1-1 MVC
import java.awt.Image;
import java.net.URL;
import javax.imageio.ImageIO;
public class Model{
private String imgUrl = "http://pic33.nipic.com/20131007/13639685_123501617185_2.jpg";
// Display the image
private Image mImage;
// Model state change listener
private OnStateChangeListener mListener;
public interface OnStateChangeListener{
void OnStateChanged(Image image);
}
public Model(a){
try {
// Preload an image as the default image during initialization
mImage = ImageIO.read(new URL(imgUrl));
} catch(Exception e) { e.printStackTrace(); }}public void setOnStateChangeListener(OnStateChangeListener listener){
mListener = listener;
}
public void loadImage(a){
new Thread(() -> {
try {
// Simulate time-consuming operations
Thread.sleep(3000);
// Get the image
mImage = ImageIO.read(new URL(imgUrl));
// pass it back to View
if(null != mListener){
mListener.OnStateChanged(mImage);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();;
}
public void clearImage(a){
// Clear the image
mImage = null;
// pass it back to View
if(null != mListener){
mListener.OnStateChanged(mImage);
}
}
public Image getImage(a){
returnmImage; }}Copy the code
As you can see, the Model class in the code implements two methods related to data storage and network requests: LoadImage () and clearImage(), which also provide a listener, OnStateChangeListener, that notifies the listener when the state of the Image changes. The injector is injected through the setOnStateChangeListener() method. And the listener, the injector, is the View.
The View that reads/clears the image in Listing 1-2 MVC
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class View extends JFrame implements Model.OnStateChangeListener{
private final JButton mButtonLoad = new JButton("Load");
private final JButton mButtonClear = new JButton("Clear");
private final JLabel mLabelImage = new JLabel();
public View(Model model){
// Load the default diagram when constructing the View
mLabelImage.setIcon(new ImageIcon(model.getImage()));
JPanel rootPanel = new JPanel();
rootPanel.setLayout(new BorderLayout());
rootPanel.add(mLabelImage, BorderLayout.CENTER);
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new FlowLayout());
btnPanel.add(mButtonLoad);
btnPanel.add(mButtonClear);
rootPanel.add(btnPanel, BorderLayout.SOUTH);
setContentPane(rootPanel);
pack();
setTitle("MVC");
setVisible(true);
}
@Override
public void OnStateChanged(Image image) {
if(null == image){
mLabelImage.setIcon(new ImageIcon());
}else{
mLabelImage.setIcon(newImageIcon(image)); }}public void addLoadListener(ActionListener listener){
mButtonLoad.addActionListener(listener);
}
public void addClearListener(ActionListener listener){ mButtonClear.addActionListener(listener); }}Copy the code
The above code we look at the basic class are from Java Swing, used to build the Java desktop version of the program, now use very few, we do not understand also do not need to do too much attention.
The View implements the listener interface OnStateChangeListener in the Model, This means that when the Model’s setOnStateChangeListener(OnStateChangeListener Listener) method is called, the View can be injected into the Model as a parameter, This enables the View to be notified when the image state changes in the Model.
For the purpose of the addLoadListener() and addClearListener() methods, let’s look at the Controller.
The Controller in Listing 1-3 MVC that reads/clears images
public class Controller{
private Model mModel;
private View mView;
public Controller(Model model, View view){ mModel = model; mView = view; mView.addLoadListener(e -> model.loadImage()); mView.addClearListener(e -> model.clearImage()); }}Copy the code
As you can see, the Controller manages the Model and View, and calls the addLoadListener() and addClearListener() methods on the View to set the listener for Button. Call Model to load or clear images when the user clicks.
An example is shown below.From the above code examples, you can see that the typical MVC architecture is different from the familiar MVC architecture (Model 2), where the Model and View are strongly coupled or even injected into each other.
Later, with the popularity of Web applications, the typical MVC architecture quickly evolved into the Model 2 architecture.
(It should be noted that some books, and most tech blogs that compare MVC to MVP, stop there. This is inappropriate, since subsequent MVC evolutions are the key to MVP evolutions.)
3. Familiar MVC Architecture (Model 2)
The MVC of the Model 2 structure is shown below.
This structure can be understood from JavaWeb type programs.
In JavaWeb, Javabeans, JSP, and servlets correspond to M, V, and C respectively.
1. When the user has interactive requests on the JSP page of Browser, these interactive requests will be handed to the Controller by Browser, and the Controller will operate the Model according to the actual situation;
2. When the Model operation has a result feedback, it will feed back to the Controller;
3. The Controller gives it to the View;
4. Finally, View updates UI on Browser and feeds back to users.
In this structure, the Controller completely becomes the middleman between the Model and View. The Model and View are completely decoupled, which makes it adaptable to the complex and changeable logic of Web applications. This is the advantage of this structure.
However, this structure also has disadvantages.
As you can see, capturing events in the UI is done by the Controller, and updating the UI is done by the View, but the Controller and the View are separate, This is unimaginable in Android, iOS, WP, Symbian and other applications with powerful and sophisticated GUI frameworks. Because in this GUI framework, the View is so closely related to the events in the View that they are almost inseparable.
Let’s take Android as an example. The XML file is the View, the Activity is the Controller, and the Bean, DB, and Network are the models. It is well known that UI control event handling and UI interface rendering updates are almost impossible to separate. As a result, this phase of MVC architecture applied to GUI frameworks like Android is equivalent to:
xml + Activity == View + Controller Model == Model
A lot of people who say they used MVC structure in the development of Android APP basically apply it in this way.
There’s nothing wrong with that. The selection of technology, there is no optimal, only the most appropriate.
4. Further evolution of MVC structure (Model 2, View 2, Controller 2)
We need to pay attention to the MVC of Model 2 structure mentioned above. As the UI and logic of the page become more and more complex, the code volume of Activity, which is equivalent to View + Controller structure, will become more and more large, and the class will become more and more bloated, and the maintenance will become more and more difficult.
In other words, the MVC of the Model 2 architecture is not suitable for medium to large projects. As a result, MVC has a new evolution.
The Controller was split, and the part responsible for user interaction was merged with the View to form a new View (let’s call it View 2), Separate the remaining logical parts of the Controller into a new Controller (let’s call it Controller 2), leaving Model 2 unchanged.
This structure — shown below — already looks a lot like the MVP structure we’ll focus on later.
MVP structure appearance and deformation
In fact, there is no need to explain the evolution of MVC structure in the stages of Model 2, View 2 and Controller 2, because that is the simplified MVP structure:
A brief definition:
M and V are also Model and View, while P is the English word for Presenter, which translates to “Presenter” and acts as a bridge of communication between Model and View.
In addition, there is a Contract that comes with MVP, which is usually used as an interface to manage the View interface, Presenter interface, and even Model interface.
Two more things to say before we talk about MVP: 1. MVP structure is just a branch or evolution of MVC structure, and currently there is no standardized model for MVP (of course, MVC does not necessarily have one). What model you choose in your development may change with your application of MVP. Again, there is no optimal, only the most appropriate. 2. The design of MVP structure is closely related to object-oriented design principles, such as single responsibility principle, open and closed principle, dependency inversion principle, etc. If you are not familiar with these principles, you are advised to supplement the knowledge first.
Okay, let’s get to the point and introduce some of the MVP variations, which are some of the tweaks I’ve made in the process of MVP application.
1. Typical MVP structure
This is the typical MVP structure that you see in most articles on the MVP structure, and I wrote a little hand-written demo that looks up examples of computer configuration from the web to help us understand it.
Query the computer configuration Contract under MVP in Listing 2-1
import java.util.List; public interface Contract{ public interface IPresenter{ void getComputerComponents(); } public interface IModel{ void getComputerComponents(INetworkCallback<List<String>> networkCallback); } public interface IView{ void onSuccessGetComputerComponents(List<String> componentsList); void onFailGetComputerComponents(String errorMsg); }}Copy the code
As mentioned above, Contract exists as a Contract, which is responsible for the unified management of View, Model and Presenter, and the relevant functions and methods can be clearly known.
Listing 2-2 Shows the result of the network request INetworkCallback under MVP
Public interface INetworkCallback<T>{void onSuccess(T result); / / public interface INetworkCallback<T>{void onSuccess(T result); void onFail(String errorMsg); }Copy the code
With generics, when a network request in the Model succeeds, the result of the request is returned to Presenter
Query the computer configuration Model under MVP in Listing 2-3
import java.util.ArrayList; import java.util.List; public class Model implements Contract.IModel{ @Override public void GetComputerComponents (INetworkCallback<List<String>> networkCallback) {new Thread(() -> {try {// simulate time-consuming operation Thread.sleep(3000); List<String> componentsList = new ArrayList<>(); componentsList.add("Wired keyborder"); componentsList.add("Wired mouse"); Return the result to Presenter if(null! = networkCallback){ networkCallback.onSuccess(componentsList); } } catch (Exception e) { e.printStackTrace(); } }).start();; }}Copy the code
Query the Presenter configuration of the computer under MVP in Listing 2-4
import java.util.List; import Contract.IView; public class Presenter implements Contract.IPresenter{ private Contract.IModel mModel; private Contract.IView mView; public Presenter(Contract.IView view){ mModel = new Model(); mView = view; } @Override public void getComputerComponents() { mModel.getComputerComponents(new INetworkCallback<List<String>>(){ @Override public void onSuccess(List<String> componentsList) { mView.onSuccessGetComputerComponents(componentsList); } @Override public void onFail(String errorMsg) { mView.onFailGetComputerComponents(errorMsg); }}); }}Copy the code
As you can see, Presenter has instances of the View interface and Model interface that act as a bridge between the View and Model.
Query the View of computer configuration under MVP in Listing 2-5
import java.awt.BorderLayout; import java.awt.FlowLayout; import java.util.List; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; public class View extends JFrame implements Contract.IView{ private JButton mButton = new JButton("Get Computer Components"); private Contract.IPresenter mPresenter; private JDialog mDialog; private JTextArea mTextArea; public View(){ mPresenter = new Presenter(View.this); JPanel rootPanel = new JPanel(); rootPanel.setLayout(new BorderLayout()); JPanel contentPanel = new JPanel(); contentPanel.setLayout(new FlowLayout()); contentPanel.add(mButton); mButton.addActionListener(e -> { mPresenter.getComputerComponents(); }); rootPanel.add(contentPanel, BorderLayout.SOUTH); mDialog = new JDialog(); mDialog.setTitle("Computer Components List"); mDialog.setSize(400, 300); mDialog.setLocation(100, 100); setContentPane(rootPanel); pack(); setTitle("MVP"); setVisible(true); } @Override public void onSuccessGetComputerComponents(List<String> componentsList) { StringBuilder stringBuilder = new StringBuilder(); for(int index=0; index<componentsList.size(); index++){ stringBuilder.append(componentsList.get(index)+"\r\n"); } mTextArea = new JTextArea(); mTextArea.setText(stringBuilder.toString()); mDialog.add(mTextArea); mDialog.setVisible(true);; } @Override public void onFailGetComputerComponents(String errorMsg) { } }Copy the code
View or use Java Swing, in the constructor to build a Java desktop version of the program, specific Swing controls we do not need to care too much. Note that the View handles the event handling of the button control and then calls Presenter to handle the business logic.
Listing 2-6 shows the Main of the computer configuration in MVP
public class Main{ public static void main(String[] args) { Contract.IView mView = new View(); }}Copy the code
So much for the MVP code example, which is relatively simple and logically clear.
Now please recall that the evolution of MVC mentioned above, from step 3 Model 2 to step 4 evolution of Model 2, View 2 and Controller 2, Controller 2 is split, View 2 is responsible for UI interface and user interaction. Model 2 handles Data operations such as Data access and network requests, while Controller 2 concentrates on other business logic.
This division of labor is typical of the MVP structure, where the UI and user interaction are all handed over to the View, and the business logic is all handed over to the Presenter.
2. Several variations of the MVP structure
Here are a few variations of MVP. In fact, it can’t be called deformation, but it might be more appropriate to say that the MVP code structure of small adjustments, small tricks.
(1) The MVP structure is independent of each other. That is, every Activity, Fragment, etc. that has data access and network operation has almost one of its corresponding MVP interfaces and implementation classes.
For example, MessageListActivity and MessageDetailActivity have their own MVPC.
(2) Business modules use unified Contract and Model, in which each Activity and Fragment with data access and network operation has its own independent View and Presenter. Take the code for the message Msg module as an example:
Listing 3-1 shows an example of MVP code for the message module
Public interface MsgContract {interface Model extends IBaseModel {/** * query message list * @param networkCallback */ void getMsgList(INetworkCallback<MsgListBean> networkCallback); * @param networkCallback */ void getMsgDetails(String msgId, INetworkCallback<MsgDetailsBean> networkCallback); } interface MsgListView extends IBaseView { void onSuccessGetMsgList(MsgListBean msgListBean); } interface MsgListPresenter { void getMsgList(MsgReqParamsBean msgReqParamsBean); } interface MsgDetailsView extends IBaseView { void onSuccessGetMsgDetails(MsgDetailsBean msgDetailsBean); } interface MsgDetailsPresenter { void getMsgDetails(String msgId); }}Copy the code
Write in the last
The evolution of architecture patterns like MVC and MVP is the evolution and crystallization of the coding experience and software design thought of many senior software engineers over the decades. As long as the evolution path of MVC is clear, the understanding and application of MVP will become easy and easy.
In addition, whether it is MVC or MVP, or object-oriented design, or the 23 design patterns, we must practice and think more, so as to avoid the situation that “it seems that you can use it, but you cannot explain it clearly”.
Thank you
- “Android source code design mode analysis and combat” He Honghui Care people, etc
- Android Advanced Light by Liu Wangshu