Note: Some people secretly ask me that MVP is not BaseActivity. In fact, I want to say that this article is not about MVP, and the initial experience of MVP mentioned in this article focuses on the idea of MVP rather than the concrete implementation. And so far no one has said that the MVP implementation is necessarily what it is, and one of the recommendations in my project is that the View layer needs to extract a BaseView for Presenter’s fixed hold, and there are a lot of business Views (interface isolation principle), A temporary parameter for a specific business method passed in as Presenter. Of course, our project uses RxJava and RetroFIT. BaseView implemented in BaseActivity is mainly used to manage CompositeSubscription in RxJava. It depends on the actual situation whether you need to implement BaseView, that is to say, is it necessary for Presenter to hold a fixed View, at least I don’t think it’s inevitable, to have the View as an interface in a dissociated state, like a building block, Concrete classes implement this when needed and are not limited to activities and fragments.

The outline

I have written a lot of codes, which have never been sorted out and recorded. Over time, I will always forget them. What’s more, the daily knowledge is always scattered, which is difficult to become a system and a natural reaction of personal daily behavior. In fact, I have always believed that if a person is faced with everything, no matter it is difficult, or very simple, he can naturally give a plan subconsciously. First of all, no matter what is the best, I should at least have my own experience and ideas. Of course, I should constantly strengthen this subconscious judgment in continuous learning, so that I can feel that life is a legend. Back to business, this article mainly records my personal understanding of the program design six principles, in the collation of time also read a lot of relevant articles on the Internet as a reference.

Six principles of programming

First, let’s look at what the six principles are:

  • Single responsibility principle
  • Richter’s substitution principle
  • Dependency inversion principle
  • Interface Isolation Principle
  • Demeter’s rule
  • The open closed principle

These six principles are more or less familiar, and we do not cover them all when we type code. The reason is not that the principles are not necessary, but that some of them may differ from each other, so we need to use them flexibly. Overzealous pursuit of interface isolation, for example, can end up being a disaster, especially for large projects.

Single responsibility principle

SRP(Single Responsibility Principle) There should never be more than one reason for a class to change No more than one factor can affect a class change. In other words, when we write code for a class implementation, we should try our best to only serve one thing or one object, rather than having it provide methods for multiple objects or things at the same time.

It is really not easy to explain clearly, mainly a lot of things or a practical distinction, or distance to explain it

case

To achieve a landing page, there are two input boxes in the landing page are the user name, password input and a button for landing request, the input content will be passed to the server, the server returns the results after the page to give the user prompts or other interactive response.

This scenario is not uncommon, and the function is very simple there is no good concern, directly start to write, such as Android: 1, directly write layout after the layout file; 4. Judge the result of the request and give feedback. The above implementation is implemented in LoginActivity. Some people (and I did this early in the morning) just do everything in onCreate.

Analysis of the

The above solution has no mistakes, the current requirements are very simple and the amount of code is not too much, and it is really not technical and the business process is very clear. However, the problem is that third-party login is the basic function of APP at present. The product requires the addition of third-party function, and the third-party authentication interface is added to the background server. We need to carry out iterative maintenance and upgrade the new demand!

When you see a new interface request, add a few third-party login buttons, and then continue to complete in onCreate, and then test ok, you will have to look at the code ugly and shake your head. Not to mention later products need to add user name, mobile phone number, email to log in at the same time, need to authenticate the client to call different interfaces, then you will be even more crazy.

The magic of programming principles is that they make things easier, not just for immediate requirements, but for analysis and maintenance of future changes

At the beginning of the MVP experience

MVP is one of the most talked about patterns in 2017. I don’t know if it’s programming or code, but it seems a little bit wrong. MVP hasn’t had a formula since it came out, and I didn’t even follow that formula. Following the above case, we continue to analyze:

1. In Android, Activity is mainly used for page display and user interaction, so we can carry out related layout and event monitoring; 2. The user needs to request the network to log in and obtain the login result data, but due to the uncertainty of the business, or the later maintainability, we put the interface request and Activity in different classes to realize LoginModel; 3. How does the Activity pass data to the LoginModel? Or if we can pass in a LoginModel, we can let the Activity decide for itself in the response event to call the Activity’s LoginModel object method (the LoginActivity holds the LoginModel object), However, it may lead to too much code for the Activity, and the Activity itself has some event and life cycle maintenance code, which makes the Activity even more bloated. At this point, we’ll isolate this business code and create a LoginPresenter class that holds references to LoginActivity and LoginModel, respectively

I don’t know if that makes sense to you, but the case above and the final analysis. Maybe the text is a little wordy, but WHAT I want to say is that we can divide the process into three layers according to MVP in order to facilitate maintenance and scalability. LoginModel is mainly responsible for user data acquisition, that is, network request. LoginActivity is responsible for maintaining the Activity life cycle and user interaction, while LoginPresenter is responsible for sorting out business scenarios, such as verifying whether the input is a mobile phone number or an email address, and calling LoginModel methods

conclusion

  • Class complexity is reduced, and what responsibilities are implemented are clearly defined
  • Readability increases and complexity decreases
  • Increased maintainability
  • Risk reduction due to change

We are willing to accept the good stuff, but we should also tolerate some problems. For example, we will find that the number of classes increases, which may increase the workload on individual projects to some extent. So aside from design patterns, I personally think that this single principle is more of a programming habit in terms of everyday coding

Richter’s substitution principle

For object oriented, this is certainly not unfamiliar, I first encountered this when I was studying C#, there is a very important concept in object oriented, that is inheritance. In inheritance, after a subclass extends from its parent, all methods and properties of the parent can be inherited by the subclass. So there’s actually a question about whether properties that are declared in private are inherited as well? In fact, my understanding is that there are explicit and implicit inheritance processes. I always consider private as implicit inheritance for subclasses, otherwise why can relevant attribute information be obtained for specific methods? Of course, if you think of the encapsulation of the parent class as getting information through methods, it’s also true. So this topic I think how to say also have no big problem.

concept

  • Parent class declaration, child class instantiation
  • Subclasses can appear wherever the parent class appears, but not the other way around
  • Method overrides, overloads.

That is, whenever we need to pass in or use a subclass, we try to declare it as a parent class instead of a subclass, or as an input parameter. The obvious reason is for method and business extensibility.

case

Following the example above, when the user clicks the login button to make a network request, we need to put up a dialog box to remind the user to wait for the result of the login request, and all operations that contain network request and time consuming need to pop up a dialog box to say “in progress …………”. .

implementation

We can create a BaseActivity, implement showProssGresss(String STR) in BaseActivity, and have LoginActivity inherit BaseActivity. The logic is handled directly in LoginPresenter

So in this process, we change very little, and only need to deal with the LoginPresenter business code, and other needs to implement the same requirements only need to be BaseActivity subclasses

When applying the Richter substitution principle in a project, try to avoid the “personality” of a subclass. Once a subclass has a “personality”, the relationship between the subclass and its parent class becomes difficult to reconcile. Using a subclass as a parent takes away the “personality” of the subclass, and using a subclass directly as a business makes the coupling between code confusing! As for the content of this idea, it is still a matter of actual situation. For example, BaseActivity above, it is difficult to restrict subclasses strictly because it is a personalized extension. Of course, if you make BaseActivity inherit from a SuperBaseActivity, I don’t have a problem with that either

conclusion

We can not only inherit from the parent class, but we can also use the interface to define the constraints to implement the class. In the declaration, we use the corresponding interface declaration to implement the class instantiation so that we can fulfill the above requirements. In MVP, V layer is passed into P layer more through abstract interface, in order to facilitate the diversification of V layer in P, while maximizing the use of P layer, in order to reduce the amount of code to a certain extent.

Dependency inversion principle

In fact, this part of the content of nothing, we combined with the above case, first put forward a question. I don’t know if you’ve ever used EventBus, but when we implement an EventBusActivity, If an Activity inherits EventBusActivity, it automatically registers and unregisters EventBus listeners. If an Activity receives EventBusActivity, the LoginActivity receives EventBus listeners. (Of course, it’s not too difficult to register locally.)

concept

Dependence Inversion Principle DIP High level modules should not depend upon low level modules,Both should depend upon abstractions ,Abstractions should not depend upon details,Details should depend upon abstractions

The high-level module part cannot depend on the low-level module, both should depend on the abstract definition, the abstract part cannot depend on the concrete implementation, the concrete implementation module needs to depend on the abstract part. Let’s just go through it. I don’t know exactly how to do it, but let’s just say how to solve the problem of receiving EventBus.

To solve the problem

EventBusActivity we extracted and abstracted into an interface IEventBus,

public interface IEventBus {
}
Copy the code

Then, the corresponding method in BaseActivity is used to determine whether IEventBus is implemented. If so, it is registered in EventBus onCreate

if (this instanceof IEventBus) {
EventBus.getDefault().register(this);
}
Copy the code

In the onDestroy

if (this instanceof IEventBus) {
            EventBus.getDefault().unregister(this);
        }
Copy the code

Then you just have the LoginActivity declaration implement IEventBus

This principle mainly emphasizes the use of more abstract applications for relational control in our coding process, reducing the direct dependence of entity classes. Abstract interfaces are easier for classes to manage and maintain.

Interface Isolation Principle

The isolation principle for interfaces is similar to the single principle. We can understand that in the interface that we define, all interfaces are constrained and defined for only one function or operation as far as possible.

define

A client should not rely on interfaces it does not need; The dependency of one class on another should be based on the smallest interface

Unlike but similar to the single responsibility principle, the single responsibility principle requires that the definition of interfaces and classes focus on the implementation of certain functions. Interface isolation requires that the definition of an interface avoid complex, bloated (that is, too many methods), ideally one interface for one method!

Ask questions

I’m going to present a scenario here, and I’m going to continue with the case above. On the login page, after the user clicks login, we need to pop up different prompt contents according to the results returned in the background. Meanwhile, if the user logs in to a third party, we need to prompt the user that the current account is not bound with mobile phone number on the current page, so that the user can choose to unbind mobile phone number or unbind other existing accounts. If the login fails, we will return the actual picture verification code according to the background parameters. The user needs to input the correct picture verification code when clicking login next time to respond to the button request again

To analyze problems

By default, LoginActivity inherits BaseActivity from LoginPresenter. LoginPresenter controls the display and hiding of dialog boxes, but the display of binding and image captcha are not public. It is not suitable for BaseActivity, and the operation of image captchas is not a dialog box and requires layout support to some extent.

This might not be easy to do, because LoginPresenter seems to just hold a BaseActivity object, even though the actual LoginActivity implementation doesn’t call the showImageNotifyCode method of The LoginActivity directly. So what to do? There is no shortage of such scenarios in real projects. My solution is to abstract the interface LoginNotifyListener, which is provided by the login method in LoginPresenter with an input parameter LoginNotifyListener. So the question is how do I define LoginNotifyListener? One might just define two methods, choiceBindOption and showImageNotifyCode. Careful friends found that, in fact, the image verification code requirements are not only here may be used, mainly used to control the access rate of the server, such as registration, access to verification code and so on what occasions may be used, get at that time we also have to create different interfaces? Of course the answer is no! At this time we can profoundly understand the interface segregation principle the usefulness of the interface segregation in order to let us only make us more clear in the process of relationship between reference we need to do We can declare two interfaces BindAccountOptionListener and ImageNotifyCodeListener respectively, The method is choiceBindOption and showImageNotifyCode, and LoginNotifyListener inherits from both interfaces. Finally, LoginActivity implements the LoginNotifyListener interface

conclusion

We don’t know if you will encounter the above case in the project, but we think it is relatively common. We don’t need to emphasize the interface isolation principle too much, but just follow the common principle when there is no actual need for it, because the interface isolation principle can lead to too many interfaces.

Demeter’s rule

One object should have minimal knowledge of other objects

There’s nothing particular to say about this, because this is like an emphasis from the previous one, right

Demeter’s law is more like a summary and sublimation of the previous Richter’s substitution principle and dependency inversion principle. Interface oriented programming is better than implementation oriented programming. Interface programming is more consistent with Demeter’s law according to Richter’s substitution principle

The open closed principle

A software entity such as a class, module, or function should be open for extension and closed for modification

Analysis of the

This is a fundamental design principle, but one of the most ill-defined, open to extension and closed to modification. Specific how to operate that is a matter of opinion for abstract programming, abstract construction of the entire system framework, the implementation of the expansion of specific logic details. That said, is there refactoring once the framework changes? This is a synthesis of design principles, the first five principles have been well used, then I’m afraid it is the open and closed principle,

conclusion

Now let’s summarize the six principles. First of all, we emphasize that the six principles are not necessarily followed in actual projects, but the key lies in their practical application.

As mentioned earlier, the overemphasis on the single principle and interface isolation principle inevitably leads to a proliferation of classes and interfaces, which is not a good thing for some teams. So these six principles are more like a standard of daily programming, to be precise, the six principles of programming, more or less understand the design pattern of the above should be comparable to several.

Every programmer wants to do the right thing in the actual project situation, but for frameworks, project programmers should be careful how they choose between product development and project design stability. My personal opinion is that the gradual iteration of complex projects is not only conducive to the market trial of the product itself, but also not eager to fix its own development direction. After all, any idea needs the market to confirm it is the king. Of course, the key is that we can better control the risk of project design, and strive to achieve small risks, small changes and small emotions