在描述我們解決組件化關(guān)于接口隔離方法之前,先拋出兩個(gè)問(wèn)題:
??1. 大家如何拆分非業(yè)務(wù)模塊的模塊,比如用戶(hù)信息模塊,vip信息模塊等非業(yè)務(wù)模塊。
??2.大家對(duì)于接口和模型的理解,如何訪問(wèn)接口的屬性,以及如何實(shí)現(xiàn)KVO監(jiān)聽(tīng)接口屬性變化(比如我們經(jīng)常使用的RAC操作)。
??3.如何管理那些protocol,怎么在外部調(diào)用那些實(shí)現(xiàn)接口的類(lèi)的方法以及屬性。
一、關(guān)于模塊化的拆分
??目前在我們APP里面,實(shí)際上是集合了連個(gè)APP,一個(gè)是學(xué)生版,一個(gè)是教師版。學(xué)生端的功能 是純Native的代碼,教師端的是RN的代碼,但是被集成我們的APP中。教師功能有一些模塊是和學(xué)生端是共用的,比如用戶(hù)信息,埋點(diǎn)系統(tǒng),設(shè)置頁(yè)面等等。
??由于在之后的版本迭代里面,教師端的功能,會(huì)從我們的主APP中遷移出去,但是有些功能在教師端還能用,我們我們必須要實(shí)現(xiàn)組件化。下面是我們組件化的簡(jiǎn)單結(jié)構(gòu)圖,今天主要是針對(duì)關(guān)于user,vip這兩個(gè)模塊的抽取進(jìn)行講解。大家主要看框圖上黃色的位置。

下圖是我們根據(jù)上面的拆分方式,加上我們的對(duì)業(yè)務(wù)的拆分之后,整理出來(lái)的結(jié)構(gòu)。這些代碼目前在pod里面,很大家做組件化一樣,我們使用的也是podspec,具體我就不寫(xiě)了。

二、關(guān)于接口的設(shè)計(jì)
通常情況下,我們?cè)贏PP中會(huì)有一個(gè)userManager這個(gè)單例去管理我們整個(gè)APP中用戶(hù)信息變化。然后在userManager實(shí)現(xiàn)對(duì)應(yīng)的增刪改查,屬性訪問(wèn)等操作。在做組件化之后,我們有一個(gè)userManagerProtocol這個(gè)接口包含所有原來(lái)userManger相關(guān)屬性和方法,為了能夠讓大家在調(diào)用用戶(hù)信息的時(shí)候感覺(jué)沒(méi)有太大的區(qū)別。因?yàn)槭枪敬a,不方便透露,所有我就寫(xiě)了一個(gè)簡(jiǎn)單的demo,來(lái)解釋我們的實(shí)現(xiàn)。這是改造之前userManger頭文件。
@class YCUser;
@interface YCUserManager : NSObject
@property (nonatomic, assign) BOOL isStudent;
- (NSDictionary *)retunUserInfo;
- (NSString *)returnToken;
// 更新
- (void)updateUserInfo:(NSDictionary *)userInfo;
- (void)updateUserToken:(NSString *)token;
- (YCUser *)returnUser;
@end
改造之后,userManger頭文件,是的什么都沒(méi)有了,
@interface YCUserManager : NSObject
@end
但是我們多了一個(gè)userManagerProtocol,為了能夠快速訪問(wèn)到用戶(hù)屬性,我們還定義了一個(gè)userProtocol,userProtocol這個(gè)接口里面的屬性和user里面的屬性保持一致。目的是為了實(shí)現(xiàn)在外部和訪問(wèn)類(lèi)的姿勢(shì)保持一致。 頭文件如下:
@protocol YCUserManagerProtocal <NSObject, BHServiceProtocol>
@property (nonatomic, assign) BOOL isStudent;
- (NSDictionary *)retunUserInfo;
- (NSString *)returnToken;
// 更新
- (void)updateUserInfo:(NSDictionary *)userInfo;
- (void)updateUserToken:(NSString *)token;
- (id<YCUserProtocol>)returnUser;
@end
對(duì)于userManger實(shí)現(xiàn)類(lèi)如下:
#import "YCUserManager.h"
#import "YCUserManagerProtocal.h"
#import "YCUser.h"
#import <MJExtension.h>
#import <BeeHive.h>
@BeeHiveService(YCUserManagerProtocal, YCUserManager)
@interface YCUserManager() <YCUserManagerProtocal>
@property (nonatomic, strong) YCUser *studentUser;
@property (nonatomic, strong) NSDictionary *userDict;
@property (nonatomic, copy) NSString *token;
@end
@implementation YCUserManager
@synthesize isStudent = _isStudent;
+ (BOOL)singleton {
return YES;
}
static YCUserManager *_manager = nil;
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_manager = [[YCUserManager alloc] init];
});
return _manager;
}
- (void)updateUserInfo:(NSDictionary *)userInfo {
self.userDict = userInfo;
YCUser *user = [YCUser mj_objectWithKeyValues:userInfo];
self.studentUser = user;
self.isStudent = YES;
}
- (void)updateUserToken:(NSString *)token {
self.token = token;
}
- (NSString *)returnToken {
return self.token;
}
- (NSDictionary *)retunUserInfo {
return self.userDict;
}
- (id<YCUserProtocol>)returnUser {
return (id)self.studentUser;
}
@end
大家在上面的文件中會(huì)看到#import <BeeHive.h> ,這是阿里出的一個(gè)框架,BeeHive是基于Spring的Service理念,雖然可以使模塊間的具體實(shí)現(xiàn)與接口解耦,但無(wú)法避免模塊對(duì)接口類(lèi)的依賴(lài)關(guān)系。在我們的模塊化中主要用這個(gè)類(lèi)實(shí)現(xiàn)模塊與接口解耦的功能。在使用這個(gè)類(lèi)的時(shí)候,為了能夠找個(gè)這個(gè)類(lèi),需要注冊(cè)一下,注冊(cè)service姿勢(shì)有三種,具體關(guān)于BeeHive的更過(guò)功能以及使用姿勢(shì)可以看 [https://github.com/alibaba/BeeHive]
1. 通過(guò)BeeHiveService宏進(jìn)行Annotation標(biāo)記。
@BeeHiveService(YCUserManagerProtocal, YCUserManager)
2. 讀取本地Pilst文件
[BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";
3.Load方法注冊(cè)
[[BeeHive shareInstance] registerService:@protocol(YCUserManagerProtocal) service:[YCUserManager class]];
三、關(guān)于接口管理以及使用
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *dict = @{
@"name":@"周文超",
@"age":@(12),
@"student" : @{
@"studentName" : @"李向紅",
@"studentAge" : @(11)
}
};
id<YCUserManagerProtocal> userManager = [[BeeHive shareInstance] createService:@protocol(YCUserManagerProtocal)];
// 更新數(shù)據(jù)
[userManager updateUserInfo:dict];
[RACObserve(userManager, isStudent) subscribeNext:^(id x) {
}];
// 獲取用戶(hù)信息
id<YCUserProtocol> user = [userManager returnUser];
[userManager updateUserToken:@"我是token"];
NSLog(@"%@----%@-----%@", user.name, [userManager returnToken], [user.student mj_JSONObject]);
[RACObserve(user, name) subscribeNext:^(id _Nullable x) {
}];
}
先看代碼,對(duì)著代碼進(jìn)行講解。
id<YCUserManagerProtocal> userManager = [[BeeHive shareInstance] createService:@protocol(YCUserManagerProtocal)];
在BeeHive中是通過(guò)BHServiceManager來(lái)管理各個(gè)Protocol的。BHServiceManager中只會(huì)管理已經(jīng)被注冊(cè)過(guò)的Protocol。上面我們對(duì)YCUserManagerProtocal進(jìn)行了注冊(cè)。因此我們可以通過(guò)這種方式獲取到我們注冊(cè)的service。
大家可能疑惑為什么,對(duì)于一個(gè)接口的屬性可以實(shí)現(xiàn)KVO操作,這也是OC的特性。protocol 的本質(zhì)類(lèi)似一個(gè)抽象類(lèi),這個(gè)聲明了一些純虛方法或者屬性。在java中,這個(gè)叫接口類(lèi)在編碼中,通過(guò)繼承協(xié)議,實(shí)現(xiàn)了協(xié)議中描述的這一套方法或者方法賦值操作。我們?cè)诮涌诶锩娑x了一個(gè)isStudent屬性,在實(shí)現(xiàn)類(lèi)里面,我們做了
@synthesize isStudent = _isStudent;
所以我們可以實(shí)現(xiàn)對(duì)它的監(jiān)聽(tīng),同樣,我們通過(guò)
// 獲取user
id<YCUserProtocol> user = [userManager returnUser];
[RACObserve(userManager, isStudent) subscribeNext:^(id x) {
}];
// 監(jiān)聽(tīng)user屬性變化
[RACObserve(user, name) subscribeNext:^(id _Nullable x) {
}];
注意:大家需要注意的幾點(diǎn)
- 在使用BeeHive的時(shí)候一定要注冊(cè)
- 如果要想某個(gè)服務(wù)是單利,需要重寫(xiě)下面這個(gè)方法
+ (BOOL)singleton {
return YES;
}
3.對(duì)于實(shí)現(xiàn)接口的類(lèi),大家在做模型轉(zhuǎn)json的時(shí)候,會(huì)附帶幾個(gè)protocol的四個(gè)屬性,hash, superclass, debugDescription, description