In general, the MVP architecture is more commonly used in Android, but it can also be used in iOS. I used this pattern while refactoring a feature of the project to improve the hierarchy of previous code and to get a taste of the MVP experience.
MVP hierarchical model and interaction are shown in the figure below:
The View and model interact with each other through a Presenter, cutting off direct contact.
With this architecture, the layering is clear, but it has the disadvantage of having too many glue interfaces in Presenter.
As we know, MVP layer interaction is through the interface, presenter as the intermediary, need to implement the view to operate the model layer interface, the Model layer to operate the UI interface. When presenters implement these interfaces, they simply call the Model and View interfaces without doing anything else. This will result in a plethora of glue methods for presenters, and they will also need to implement new interfaces. As a result, when functions are complex and interfaces proliferate, presenters also have more and more interface implementations, which are not conducive to maintenance.
Let’s take a look at a simple bullet-screen example to introduce the above problems.
Interface
@protocol DanmuPresenterInterface <NSObject> @optional /// cleanChats - (void)cleanChats; @endCopy the code
@protocol DanmuViewInterface <NSObject> @optional // reload - (void)reloadTableView; @endCopy the code
@protocol DanmuDataOutputInterface <NSObject> @optional // reload - (void)reloadTableView; @endCopy the code
@protocol DanmuDataInterface <NSObject> @optional // void cleanChats; @endCopy the code
Presenter
@interface DanmuPresenter() @property (nonatomic, strong) id<DanmuDataInterface> dataManager; @property (nonatomic, weak) id<DanmuViewInterface> DanmuViewInterface; @end@implementation DanmuPresenter // The interface that presenter provides for the view call#pragma mark - DanmuPresenterInterface/// cleanChats - (void)cleanChats {[self.dataManager cleanChats]; } // Implement the Model layer call update UI interface#pragma mark - DanmuDataOutputInterface
// reload
- (void)reloadTableView {
[self.danmuViewInterface reloadTableView];
}
@end
Copy the code
In this example, the interaction is as follows:
The call to the view is as follows:
Self. PresenterInterface is Presenter [self. PresenterInterface cleanChats];Copy the code
Call in dataManager as follows:
/ / self. DanmuDataOutputInterface for presenter [self DanmuDataOutputInterface reloadTableView];Copy the code
DanmuPresenterInterface DanmuDataOutputInterface DanmuPresenterInterface DanmuDataOutputInterface DanmuPresenterInterface DanmuDataOutputInterface DanmuPresenterInterface
In fact, when cleanChats are called in danmuView, the Presenter only acts as a secondary interface to the dataManager directly called inside. This type of interface greatly increases the number of interface implementations for Presenter.
Therefore, in order to reduce glue interfaces during refactoring, consider forwarding messages directly to the corresponding instances without writing implementation methods. As shown below.
- If it is
danmuView
throughDanmuPresenterInterface
Interface (The end result is actually a call to DanmuDataInterface to manipulate the model data
) is directly forwarded todataManager
. - If it is
dataManager
callDanmuDataOutputInterface
Interface to update the UI, then directly forward todanmuView
.
// Because presenter is an intermediary, it is necessary to implement the interface of view to operate the Model layer (dataManger) and the interface of Model layer to operate the UI (chatView), which leads to too many glue methods and new interfaces. Presenter also needs a new implementation. Therefore, message forwarding is used to simplify processing. - (id) forwardingTargetForSelector aSelector: (SEL) {/ / forwarding DanmuDataInterface implementation to dataManager struct objc_method_description omd = protocol_getMethodDescription(@protocol(DanmuDataInterface), aSelector, NO, YES);if(omd.name ! = NULL) {if ([self.dataManager respondsToSelector:aSelector]) {
returnself.dataManager; } // Forwarding DanmuDataOutputInterface implementation to danmuView omd = protocol_getMethodDescription(@protocol(DanmuDataOutputInterface), aSelector, NO, YES);if(omd.name ! = NULL) {if ([self.danmuViewInterface respondsToSelector:aSelector]) {
returnself.danmuViewInterface; }}return [super forwardingTargetForSelector:aSelector];
}
Copy the code
In this way, the DanmuDataInterface and DanmuDataOutputInterface implementations in Presenter can be removed. The dataManager call is [sell.uiInterface reloadTableView], and notice that we can’t call respondto Selector because presenter doesn’t implement these methods, so it won’t go.
There are limits, however. The interface that is required to be implemented in Presenter does not do any additional logic and instead calls directly to the Model layer or UI layer implementation.
For example, the following implementation calls [self xx] separately, which is not applicable.
#pragma mark - DanmuPresenterInterface/// cleanChats - (void)cleanChats {//do something
[self xx];
[self.dataManager cleanChats];
}
Copy the code
So that’s the MVP practice.