iOS组件化(三)-添加服务层以及MVVM模式

2020-05-26  本文已影响0人  samstring

模块间的对象传输

上一片分析了MGJRouter的源码,不难发现,用MGJRouter这种以URL形式进行模块间的调用存在一个天然缺陷:

模块间传递复杂对象的时候比较困难

因为模块间以URL方式调用,对象在模块间不能直接传递,只能通过以参数的形式传递,MGJRouter里面的参数可以以字典的形式存在。假设两个模块间需要传递用户信息,内容如下
SFUserInfo.h

@interface SFUserInfo : NSObject
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, assign) int age;
@end

在两个模块间传递,发送对象时就要把对象里面的值转化成以下内容,放到字典里面去。读取时候把字典里面的字段解析出来,这是一种最简单的方式

{
"userName":"张三",
"age":10
}

但是两个模块间发送和处理如果以字典的形式去处理无疑是低效的。对于这种模块间传输复杂的对象的问题,笔者有几种解决方式

但是对于这三种方式,都会存在一定的问题

在基于自身的项目中,我采用了第三种方式。对于model信息不一致的问题,可以通过脚本去检测,如果类信息不一致,则报警告。然后再去修改model,下面给出一个Demo做示范

** 注意:下方截图和代码均来自demo **

Demo如下

其中主工程和用户模块涉及到了复杂对象的传输


QQ20200527-005845.png

用户模块在回调用户信息model给主模块的时候

 SFUserInfo *userInfo = [SFUserInfo new];
   userInfo.userName = [self.viewModel getUserInfo].userName;
   if (self.userBLock) {
       self.userBLock(userInfo);
   }
   [self.navigationController popViewControllerAnimated:YES];

主模块接收

 
   UserModuleExample_UserBLock block = ^(SFExampleUserInfo *userInfo){
       NSLog(@"demo获取用户名-%@",userInfo.userName);
   };
   [userInfo  setObject:block forKey:@"block"];
   
   [MGJRouter openURL:@"sf_user://SFUserInfoViewController" withUserInfo:userInfo completion:^(id result) {
       
   }];

这样虽然解决模块间的复杂model传输问题,但这并不是一个优秀的方案,需要基于自身的项目去选择合适的方案。

弹到了模块与模块间的数据交互,再顺便谈一下模块内的数据交互。

app的分层设计

在上面的demo中,在路由中并不是直接跳转页面,而是多加了一层Service层,由service层去做页面跳转。

  [MGJRouter registerURLPattern:@"sf_user://SFUserInfoViewController" toHandler:^(NSDictionary *routerParameters) {
        NSDictionary *userInfo = [routerParameters objectForKey:MGJRouterParameterUserInfo];
        [UserService gotoUserInfoWithController:[userInfo objectForKey:@"vc"] withBLock:[userInfo objectForKey:@"block"]];
      }];
    

整个操作逻辑如下


QQ20200527-103840@2x.png

为什么要多加这一层个service层呢?有以下几点

上面这一层服务是指模块对外访问,在模块内设计也可进行分层,如下


组件化-分层设计.png

当然分层这种设计是见仁见智的,有优点也有缺点,是否分层,怎样分层,都需要基于团队情况和项目情况而定。

app的UI层设计模式

对于UI层,我们也可以进行设计。我们常说的MVC,和MVP,MVVM等模式在我看来,更多的是针对上面所说的分层设计的UI层,令UI层尽可能的实现复用和解耦合。

关于MVC,MVP,MVVM的概念解释,网上有很多,在这不再阐述,这里推荐一篇文章
https://www.jianshu.com/p/ff6de219f988

MVVM对于MVP,其中一个很大的差异是双向数据绑定。当model或是view有所改变的时候,对应的view和model会自动的去变更。

实现这种双向数据绑定的方法有很多,例如有系统自带的KVO,ReactiveCocoa,和Facebook的开源库KVOController等。

对于系统的KVO,使用起来过于麻烦,容易出错。而ReactiveCocoa使用方便,但是实现相对来说较为复杂,功能较多,对团队的学习成本比较高。而KVOController使用相对来说简单,实现也简单,所以本文采用KVOController去实现MVVM。

在VIew或是VC初始化VIewModel。由ViewModel持有对象model,同时ViewModel持有一个VIiew或VC对象的弱引用。由VIewModel对View+ViewController和model的改变进行监听,如果有改变,则执行回调。

SFUserViewModel.h

#import <Foundation/Foundation.h>
#import "SFUserInfoViewController.h"
#import "SFUserInfo.h"

NS_ASSUME_NONNULL_BEGIN

@interface SFUserViewModel : NSObject
@property (nonatomic, weak)SFUserInfoViewController *vc;

-(void)initWtihVC:(SFUserInfoViewController *)vc;
//改变用户名称
-(void)changeUserName;
//获取用户信息
-(SFUserInfo *)getUserInfo;
@end

SFUserViewModel.m

#import "SFUserViewModel.h"
#import <KVOController/KVOController.h>
@interface SFUserViewModel ()

@property (nonatomic, strong) SFUserInfo *userInfo;
@property (nonatomic, weak) FBKVOController *kvoController;
@end

@implementation SFUserViewModel

-(void)initWtihVC:(SFUserInfoViewController *)vc{
    self.vc = vc;
    self.userInfo = [SFUserInfo new];
    self.kvoController = [FBKVOController controllerWithObserver:self];
    [self.kvoController observe:self keyPath:@"userInfo.userName" options:(NSKeyValueObservingOptionNew) block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
        self.vc.nameLabel.text = [change objectForKey:@"new"];
    }];
    [self getDataFromnet];
}

-(void)getDataFromnet{
    //模拟从网络获取用户信息
    
    self.userInfo.userName = [NSString stringWithFormat:@"用户名:%@",@"张三"];
    self.userInfo.age = 10;
}

-(void)changeUserName{
    self.userInfo.userName = [NSString stringWithFormat:@"用户名:%@",[self randomNoNumber:8]];;
}

-(SFUserInfo *)getUserInfo{
    return self.userInfo;
}

// 随机生成字符串(由大小写字母组成)
-(NSString *)randomNoNumber: (int)len {
    
    char ch[len];
    for (int index=0; index<len; index++) {
        
        int num = arc4random_uniform(58)+65;
        if (num>90 && num<97) { num = num%90+65; }
        ch[index] = num;
    }
    
    return [[NSString alloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding];
}
@end

上面的代码交互如图所示


MVVM-应用.png

如果有需要,也可以在ViewModel中监听view层UI的变化,这样可以进一步把代码封装到ViewModel中,减少VIewController中的逻辑。

最后说一下,分层设计,MVC,和MVP,MVVM这些概念,理解并不难,但是需要实践,需要应用到具体业务。并不是说越高级,越复杂越好。使用哪种模式,要看是否适用于项目,是否可扩展,可复用,耦合程度低。

上一篇 下一篇

猜你喜欢

热点阅读