MVVM的两种不同思路

思路1:7种不同角色

  • xxxViewController处于最顶级,作为调用者
  • xxxViewxxxViewModel作为xxxViewController的属性。一个负责界面显示,一个负责具体业务。xxxViewModel的主要作用是给xxxViewController减负,全面接管他的工作。这一步是MVVM思想的最大体现,看似最简单,其实最关键。以后凡事找xxxViewModel,跟xxxViewController基本上没啥关系了。(数据、业务和界面解耦了)
  • xxxUseCase作为xxxViewModel属性,完成xxxViewModel的具体工作,比如网络请求,相关业务逻辑等等。
  • xxxBuilder作为xxxUseCase的属性。主要作用是将数据xxxModel转换为适合xxxView显示的数据xxxViewBean。一般来讲,xxxModel可以作为xxxBuilder的输入参数,xxxViewBean作为xxxBuilder的返回值。
  • xxxUseCase通过xxxBuilder得到相应的xxxViewBean之后,需要更新xxxView的显示。这里的方法是通过xxxViewModel,将weak修饰的xxxViewController传过来(防止引用循环),通过他,访问相应的xxxView,利用新得到的xxxViewBean进行更新。
  • xxxViewController、xxxView、xxxViewModel、xxxUseCase、xxxBuilder、xxxModel、xxxViewBean有7种不同的角色。
  • 其他地方说的ViewModel和这里说的ViewBean有点类似。

思路2:4+1种不同角色

  • xxxViewController处于最顶级,作为调用者,同时也是被“减负”的对象
  • xxxView作为xxxViewController的属性,负责界面显示
  • xxxViewModel作为xxxView更新界面相关方法的参数,其构造方法的参数是xxxModel(可以为nil,提供默认静态显示状态)。xxxViewModel的作用是显示逻辑,对xxxModel的一次适配。这一步很简单,不过确实这里两个关键点中的一个。将原先的一些琐碎的显示逻辑从xxxViewController中转移过来。
  • yyyServicexxxViewController提供数据和业务服务。其服务对象是业务模块中的所有ViewController,多个复用。等业务成熟之后,可以隔离为yyyService.framework。调用接口,统一为类方法,具体实现是单例还是其他实例,对外部隐藏。
  • yyyService服务的返回值是xxxModel,他没有必要知道xxxViewModel的任何信息。这一步也很关键,将数据和业务逻辑和界面、ViewController解耦。
  • xxxViewController的作用是一个“调用者”。调用yyyService得到xxxModel。调用xxxView更新界面。xxxView通过xxxModel得到相应的xxxViewModel,更新显示。
  • xxxViewController、xxxView、xxxViewModel、xxxModel有4种不同角色。再加上多个共用的yyyService,一共5种不同角色。
  • 双向绑定,swift只要用属性观察器就可以了;Object-C可以采用第三方库ReactiveCocoa

隔离出一个逻辑层

  • 对于业务逻辑,采用zzzLogic的命名方式,作为第二层的抽象。这样就分为界面,逻辑,服务三层。层与层之间用协议进行沟通,通过framework进行隔离。
  • 推荐用Swift开发,一些好的特性不可获取。至于runtime,c++,c等,可以在一个framework中用Object-C作为胶水过渡一下
  • 在开发界面的时候,以ViewModel和逻辑协议与逻辑层沟通。协议定好之后,马上提供默认实现。这样整个过程就能独自前进。
  • 逻辑层实现界面要求的协议,不能访问任何UIKit的对象,同时将一些跟具体业务逻辑没有关系的功能型问题下放给服务层,定好协议,马上提供默认实现。文件组织按照业务角度划分模块,不要考虑具体的页面。(不然的话,两套标准,必然混乱)
  • 服务层就负责实现逻辑层定好的协议。按照功能分模块,不要带入任何界面和业务逻辑的概念,只按照功能一个标准组织结构。(标准不统一,必然带来混乱)
  • 在文件命名上,禁止使用temp、core、common、public、base、tool等抽象的名字,作为后缀可以考虑。
  • tableView这种界面和数据强绑定的方式需要引入协议的方式,将界面和数据分开
  • SDWebImage这种在UI上扩展数据功能的第3方库要换掉,或者改变使用方式。能干好下载一件事就可以了,不用瞎操心界面的显示
  • 类别这种方式不要直接用,中间在包一层,以函数接口的方式对外提供服务。统一到类方法的包装之中,简单好用。
  • 界面更新采用属性观察者模式,集中到观察ViewModel的变化上来,进行单向的数据绑定
  • 反向通信以block的方式为主,一对一,关系简单,使用灵活
  • 正向通信以函数返回值为主,多个值可以返回一个元组,很方便的
  • 函数参数以copy形式的形参为主,改变实参的inout参数尽量不要用
  • 在必要的时候,可以用用notification,这个要注意通知都要求在主线程发送和接收,降低复杂度,减少不必要的麻烦
  • 界面跳转采用url的形式统一编码,集中在一个文件中定义。具体的跳转实现在各自的controller中,不要用统一的switch结构
  • 库管理工具推荐Carthage,当然cocoapods也很好用,看习惯选择。
  • 环境参数也放在一个统一的plist文件中,比如友盟的APPKey,baseURL
  • 对于支付插件,分享等外接组件,统一用framework再包一层,对外提供自己定义的协议接口。不能让具体的功能实现污染统一的接口定义。
  • 界面层有需要的时候也能够访问服务层。
  • 访问顺序是单向的,不能反过来,比如服务层不能方位逻辑层和界面层;逻辑层也不能访问界面层。
  • 在必要的时候,逻辑层或者服务层能够使用界面层传过来的UI对象,但是应该采用weak引用,不能持有该UI对象。UI对象的生命周期由界面层独立负责。
  • 真正的数据驱动应该在逻辑层,界面层只是简单的界面流转,不要带入数据和业务逻辑。就像故事版的连线一样,被动流转,不主动驱动。

备注

  • 思路1已经在一些公司应用,xxxViewBean概念来自Java,相当于其他地方说的xxxViewModel
  • 思路2还没有见到实际的应用,随着iOS8和动态framework逐渐被接受,可以考虑尝试一下。
  • 本人偏向思路2,并且和动态framework结合起来用
  • 总体感觉思路1过于繁琐,就像有名的VIPER一样。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容