In order to study componentization, we mainly discuss mogujie routing + protocol pattern and middleware

We will discuss the first approach and implement a working demo with reference to Mogujie IOS componentization and discuss the pros and cons.

routing

Using MGJRouter singletons, through subscription or registration and publication or use. It’s definitely a little fuzzy, so let’s start coding.

Determine unique identifier

Through the MGJ: / / detail? Id =:id register with MGJ ://detail? Id =5, pass the parameter, first we find the similarity between the two urls, In the following code I use a simple algorithm + (NSString *)keyWithUrlStr:(NSString *)urlStr to get a unique identifier mgj_detail_id*, which can be registered and used through map

  • registerURLPattern:toHandler:registered
  • openURL:The calling component
@interface MGJRouter : NSObject
// Comments can be made in one unified place
//mgj://detail? id=:id
+ (void)registerURLPattern:(NSString *)pattern
                 toHandler:(void(^)(NSDictionary *dic))block;
// Trigger the component's callback
//mgj://detail? id=5
+ (void)openURL:(NSString *)urlStr;
@end

@interface MGJRouter()

@property (nonatomic,strong) NSMutableDictionary * map;

@end

@implementation MGJRouter

- (instancetype)init {
    if (self = [super init]) {
        self.map = [NSMutableDictionary dictionary];
    }
    return self;
}

+ (MGJRouter *)shareManager {
    static dispatch_once_t onceToken;
    static MGJRouter * router = nil;
    dispatch_once(&onceToken, ^{
        if (router == nil) {
            router = [[MGJRouter alloc] init]; }});return router;
}

// Comments can be made in one unified place
+ (void)registerURLPattern:(NSString *)pattern
                 toHandler:(void(^)(NSDictionary *dic))block {
    
    NSString * key = [self keyWithUrlStr:pattern];
    [self shareManager].map[key] = block;
}

// Trigger the component's callback
+ (void)openURL:(NSString *)urlStr {
    
    NSString * key = [self keyWithUrlStr:urlStr];
    NSLog(@"key = %@",key);
    void(^block)(NSDictionary *dic) = [self shareManager].map[key];
    if (block) {
        NSMutableDictionary * param  = nil;
        NSURL * url = [NSURL URLWithString:urlStr];
        NSString * query = url.query;
        if (query.length) {
            NSArray * items = [query componentsSeparatedByString:@"&"];
            param = [NSMutableDictionary dictionary];
            for (NSString * item in items) {
                NSArray * littleItems = [item componentsSeparatedByString:@"="];
                NSString * itemFirst = littleItems.firstObject;
                NSString * itemSecond = littleItems.lastObject;
                if(itemFirst && itemSecond) { param[itemFirst] = itemSecond; } } } block(param); }}//mgj://detail? id=:id
// Protocol name MGJ
// host detail
// query id=:id
/ / the MGJ: / / detail? Id = : id and MGJ: / / detail? Id =5 is converted to the same unique key
+ (NSString *)keyWithUrlStr:(NSString *)urlStr {
    NSURL * url = [NSURL URLWithString:urlStr];
    NSString * query = url.query;
    NSString * queryStr = @"";
    if (query.length) {
        NSArray * items = [query componentsSeparatedByString:@"&"];
        for (NSString * item in items) {
            NSString * itemFirst = [item componentsSeparatedByString:@"="].firstObject;
            queryStr = [queryStr stringByAppendingString:itemFirst];
            queryStr = [queryStr stringByAppendingString:@"*"]; }}return [NSString stringWithFormat:@"% @ _ % @ _ % @",url.scheme,url.host,queryStr];
}

@end

Copy the code

The test component

// Register a component (usually unregistered at startup)
    [MGJRouter registerURLPattern:@"mgj://detail? id=:id&name=:name" toHandler:^(NSDictionary *dic) {
        NSString * oneId = dic[@"id"];
        NSString * name  = dic[@"name"];
        if (oneId && name) {
            // Create the component and get the value from the dictionary
            DetailComposite * detail = [[DetailComposite alloc] init];
            detail.oneId = oneId;
            detail.name  = name;
            // Execute the component's methods[detail showComposite]; }}];// External calls execute a component
    [MGJRouter openURL:@"mgj://detail? id=5&name=leeDev"];
    // Print out: showComposite _ id = 5; name = leeDev
Copy the code

Summarizing routing Functions

The map is used to store the key -> component’s function block, which is called directly by passing parameters and keys through open and passing parameters.

Protocol (Protocol – Class)

Because we componentize in order not to expose our implementation classes, but we can expose some interfaces in order to reduce coupling.

Mogujie is managed through the ModuleManager protocol and class association in two main ways

@interface ModuleManager : NSObject
+ (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName;
+ (Class)classForProtocolName:(NSString *)protocolName;
@end



@interface ModuleManager()

@property (nonatomic,strong) NSMutableDictionary * map;

@end
@implementation ModuleManager


- (instancetype)init {
    if (self = [super init]) {
        self.map = [NSMutableDictionary dictionary];
    }
    return self;
}

+ (ModuleManager *)shareManager {
    static dispatch_once_t onceToken;
    static ModuleManager * router = nil;
    dispatch_once(&onceToken, ^{
        if (router == nil) {
            router = [[ModuleManager alloc] init]; }});return router;
}

+ (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName {
    
    [self shareManager].map[protocolName] = className;
}

+ (Class)classForProtocolName:(NSString *)protocolName {
    
    NSString * className = [self shareManager].map[protocolName];
    return NSClassFromString(className);
}

@end

Copy the code

Let’s say we want to get the number of shopping carts and we define a protocol

@protocol MGJCart <NSObject>
+ (NSInteger)orderCount;
@end
Copy the code

Implement the protocol in the implementation class

#import "MGjCart.h"
@interface MGJCartImp : NSObject<MGJCart>
@end


@implementation MGJCartImp
+ (NSInteger)orderCount {
    // Process the logic and return the result
    return 40;
}
@end
Copy the code

Registration and Use


// We just need to register the protocol
    [ModuleManager registerClassName:@"MGJCartImp" forProtocolName:@"MGJCart"];
    
    // From class, we just expose the MGJCart protocol
    Class cla = [ModuleManager classForProtocolName:@"MGJCart"];
    NSInteger orderCount = [(id <MGJCart>)cla orderCount];
    NSLog(@"orderCount = %@",@(orderCount));
    // Print orderCount = 40

Copy the code

The advantages and disadvantages

advantages

  • Reduced coupling means that components depend only on urls, not on specific classes

disadvantages

  • Passing object types such as image is troublesome, urls are not supported
  • Callback blocks can also be cumbersome and can be called back from dictionaries, but they need to be well documented for the user to use them properly
  • The use of protocol-classes is also cumbersome, generally the class instance is onesingletonBecause all the calls are+ method.