美文网首页
iOS组件化(三)-添加服务层以及MVVM模式

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

作者: samstring | 来源:发表于2020-05-26 23:43 被阅读0次

模块间的对象传输

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

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

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

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

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

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

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

  • 1 将所有需要传输的对象放到一个基础model组件中,所有模块去引用这个组件
  • 2 添加基于协议的模块调用,模块间对外协议中暴露model,交互的时候直接引入model
  • 3 拷贝需要传输的model到需要交互的模块中,并以模块为前缀去命名。假设A模块需要传SFUserInfo给模块B,可以在A模块新建一个A_SFUserInfo类,在B模块新建B_ SFUserInfo,A_ SFUserInfo信息和B_ SFUserInfo内容一致。在A模块传递时候将A_ SFUserInfo对象放入字典中,在模块B从字典中获取内容,并强转为B_ SFUserInfo。

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

  • 1随着模块中互相调用增加,基础model组件修改频繁,其他模块引入的时候需要频繁更新版本。并且对于其他模块,引入了很多无关的类
  • 2 可以直接受用modle,但是破坏了项目独立编译的特性
  • 3 当一个模块的modle修改时,其他模块的修改不及时,导致两边的model信息不一致。容易出现问题。而且出现代码冗余。适合于

在基于自身的项目中,我采用了第三种方式。对于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层呢?有以下几点

  • 封装模块,将操作逻辑交由service处理。在serivie层提供对外的URL,方法以及参数,其他模块在调用这个模块的时候,只需要查看模块对应的service头文件便可确定调用时候所需要的地址。
  • 如果后期要添加基于协议的路由交互方式,可以在sercie层添加。
  • 减少模块对MGJRouter的依赖。

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


组件化-分层设计.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这些概念,理解并不难,但是需要实践,需要应用到具体业务。并不是说越高级,越复杂越好。使用哪种模式,要看是否适用于项目,是否可扩展,可复用,耦合程度低。

相关文章

网友评论

      本文标题:iOS组件化(三)-添加服务层以及MVVM模式

      本文链接:https://www.haomeiwen.com/subject/phfuahtx.html