簡述元件化解決方案CTMediator與MGJRouter的主要思想

滴水微瀾發表於2019-01-22

簡述CTMediator

 
CTMediator按照功能的結構來講,使用時需要實現CTMediator的個三部分。
1.CTMediator類:承擔總樞紐,總排程的責任
2.Target_(ModuleName)類:承擔元件對外暴漏介面功能,元件要提供什麼服務,主要在它的介面宣告上進行體現
3.CTMediator+(ModuleName)分類:主要供客戶端使用,裡面宣告瞭可以呼叫的元件介面。
下面詳細講解
 
Part1: CTMediator核心功能實現:
CTMediator主要採用target-action的方式實現元件間解耦合,本身功能完全獨立,不依賴任何元件模組。
主要結構如下:
CTMediator作為中介者,是各個元件的進行資訊通訊的中樞。
主要實現方案分兩種情況:
1.首先利用runtime進行反射,將類字串和方法字串轉換成類和SEL方法選擇子:
SEL action = NSSelectorFromString(@"Action_response:");
NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
然後呼叫cocoa touch框架提供的方法直接呼叫
程式碼如下:
[target performSelector:action withObject:params];

 

2.或者使用cocoa touch提供的命令模式,將訊息和訊息接受者封裝成一個物件,進行執行。
首先,利用target-action生成方法簽名
然後,建立NSInvocation物件,進行執行invoke。並拿到返回的結果。
程式碼如下:
利用方法簽名,NSInvocation實現
NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
if(methodSig == nil) {
    return nil;
}
const char* retType = [methodSig methodReturnType];
if (strcmp(retType, @encode(void)) == 0) {
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
    [invocation setArgument:&params atIndex:2];
    [invocation setSelector:action];
    [invocation setTarget:target];
    [invocation invoke];
    return nil;
}

 

Part2: 元件對外服務介面
如果元件需要對外提供服務,就需要建立自己的接收動作類
比如ModuleA要對外提供服務。那麼就要建立一個
Target_A類,然後在Target_A類的.h檔案中宣告對外服務的介面,並在.m檔案中進行實現。
注意:Target_A類是依賴元件的。它屬於元件的一部分。
程式碼如下:
@interface Target_Mine : NSObject
- (id)Action_nativeFetchSportsResultVC:(NSDictionary *)param;
- (void)Action_remoteAlertSportsResultVC:(NSDictionary *)param;
@end
@implementation Target_Mine
- (id)Action_nativeFetchSportsResultVC:(NSDictionary *)param {
    UIViewController *vc = [[FZMineCoordinator sharedFZMineCoordinator] targetVCWithClassName:NSStringFromClass([FZSportsResultVC class])];
    if ([vc isKindOfClass:[FZSportsResultVC class]]) {
        [(FZSportsResultVC *)vc configContent:param[@"title"]];
    }
    return vc;
}

- (void)Action_remoteAlertSportsResultVC:(NSDictionary *)param {
    UIViewController *vc = [[FZMineCoordinator sharedFZMineCoordinator] targetVCWithClassName:NSStringFromClass([FZSportsResultVC class])];
    if ([vc isKindOfClass:[FZSportsResultVC class]]) {
        [(FZSportsPlanVC *)vc configContent:param[@"title"]];
    }
    
    id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
    UITabBarController *rootVC = [[appDelegate window] rootViewController];
    [rootVC.childViewControllers[0] pushViewController:vc animated:YES];
}
@end

 

Part3: CTMediator+ModuleA元件通訊實際使用類

為了實現完全解耦,這個類所有使用的所有引數全部是cocoa touch框架中定義的基本型別。
像:NSDictionary,NSString, UIImage等。
裡面按照作用分,可以分為:
模組名稱字串,模組本地呼叫方法名稱字串,模組遠端呼叫方法名稱字串
在CTMediator+ModuleA分類檔案的.h檔案中,定義了供其他模組使用的介面
在CTMediator+ModuleA分類檔案的.m檔案中,實現供其他模組使用的介面,呼叫用CTMediator的runtime機制進行實現。
 
CTMediator提供的方案是我認為最好的,巧妙的使用了cocoaTouch提供的反射機制,方法簽名與命令模式,簡單又完美的解決了元件間的解耦問題。
同時因為實現是基於Object-C的特性,穩定性靠譜。
在方案不同作用類分工上,簡單明瞭。實現了從形式到實質上完全的解耦,同時提供了對外部appURL呼叫的支援。是非常完美的方案。
程式碼如下:
- (IBAction)goSportsPlanDetail:(UIButton *)sender {
    UIViewController *vc = [[CTMediator sharedInstance] Mediator_fetchSportsPlanVC:@{@"title":[sender currentTitle]}];
    [self.navigationController pushViewController:vc animated:YES];
}
CTMediator提供的方案是我認為最好的,巧妙的使用了cocoaTouch提供的反射機制,方法簽名與命令模式,簡單又完美的解決了元件間的解耦問題。
同時因為實現是基於Object-C的特性,穩定性靠譜。
在方案不同作用類分工上,簡單明瞭。實現了從形式到實質上完全的解耦,同時提供了對外部appURL呼叫的支援。是非常完美的方案。
 
簡述MGJRouter
 
蘑菇街元件化方案,採用了url-block加protocal-class的方案,url-block用於頁面跳轉,protocal-class用於元件跳轉
下面對MGJRouter的主要思路進行分析。
 
MGJRouter核心功能實現
Part1:
MGJRouter的url-block實現方案思路為,在路由中心維護著一張路由表,url為key, block為value。
註冊路由表時,將key和value對應儲存到路由表routes中
使用時,根據URL拿到對應的block進行執行。
 
- (NSMutableDictionary *)routes
{
    if (!_routes) {
        _routes = [[NSMutableDictionary alloc] init];
    }
    return _routes;
}

但是URL對應像UIImage,NSData這樣的非常規物件是很難傳遞的。

 
Part2:
蘑菇街的protocal-class實現方案思路為:
在ModuleManager內維護著一張對映表,以protocol為key,以Class為Value。
註冊對映表
[ModuleManager registerClass:ClassA forProtocol:ProtocolA]

使用對映表

[ModuleManager classForProtocol:ProtocolA]
注意:上面一一對應的關係中,類是實現了對應的協議的。所以通過協議拿到的類是可以按照protocol中宣告的方法自由使用的。
 
 
註冊步驟:
1.url-block方案註冊:
在模組對應要展示的頁面中,在load方法中進行註冊
+ (void)load {
    [MGJRouter registerURLPattern:@"engineer://SportsPlanVC" toObjectHandler:^id(NSDictionary *routerParameters) {
        FZSportsPlanVC *planVC = [FZSportsPlanVC new];
        [planVC configContent:routerParameters[@"MGJRouterParameterUserInfo"][@"title"]];
        return planVC;
    }];
}
2.protocal-class方案註冊:
在模組的協議實現類中進行註冊:
+ (void)load {
    [[FZProtocolMediator sharedFZProtocolMediator] registerProtocol:NSProtocolFromString(@"FZModuleMineProtocol") forClass:[FZModuleMineProtocolImplete class]];
}
 
使用步驟:
根據對應的單例獲取方式,獲取既可。
 
- (IBAction)mgj_goSportsPlanDetail:(UIButton *)sender {
    UIViewController *vc = [MGJRouter objectForURL:@"engineer://SportsPlanVC" withUserInfo:@{@"title":[sender currentTitle]}];
    [self.navigationController pushViewController:vc animated:YES];
}
- (IBAction)protocol_class_goSportsPlanDetail:(UIButton *)sender {
    Class<FZModuleMineProtocol> class = [[FZProtocolMediator sharedFZProtocolMediator] classForProtocol:NSProtocolFromString(@"FZModuleMineProtocol")];
    UIViewController *vc = [class fetchSportsPlanVC:sender.currentTitle];
    [self.navigationController pushViewController:vc animated:YES];
}
MGJRouter實現方案上有些複雜,使得新手學習上有些困難,同時兩張表也增加了維護成本。
不過不可否認的是url-block和protocal-class都是非常巧妙的解耦方案。
 
使用效果如下:

 

相關文章