论MVVM伪框架结构和MVC中M的兑现机制。《论MVVM伪框架结构和MVC中M的贯彻机制和M层的构建》阅读笔记。

目录

《论MVVM伪框架结构和MVC中M的实现机制与M层的构建》阅读笔记
原稿链接

  • MVC概论【本文】
  • 模型层设计艺术【请参见:http://www.jianshu.com/p/fce02188edec】
  • 控制层的规划方式【敬请期待】
* 业务类中的属性设计为只读。
* 数据模型类中的属性定义最好也设置为只读,因为数据模型的建立是在业务类方法内部完成并通过通知或者异步回调的方式交给使用者。而不应该交由使用者来创建和更新。
* 数据模型类一般提供一个带有所有属性的init初始化方法,而初始化后这些属性原则上是不能被再次改变,所以应该设置为只读属性。

直白都有人撰文吹捧MVVM应用开发框架,文章将MVVM说之好听并且批评包括iOS和android所用之MVC经典框架。这篇稿子就是纪念被那些捧臭脚的人们泼泼冷水,虽然发生或引致骂声一切开,但是目的是让那些刚入门的同伴一些参照和建议,以免误入歧途。同时也于那些深陷其中不能自拔的伴侣们敲敲警钟,以免其以错的征程上越走越远。

  1. 好之框架中列层次的拆分并无是简约的将代码进行归类和分叉,层次的分割是横向的,而模块的分是纵向的。
  2. 视图负责展示及渲染,模型负责作业逻辑的兑现,控制负责调度视图的波及工作逻辑的调用以及通报视图的刷新通知。
  3. 匪能够用M层简单明了呢平淡的只有属性没有辙的数据模型。
  4. M层是事情模型层而未数据模型层。
  5. 美妙的利用及框架并无以代码的数码,而是完全系统的代码简单容易读,各有任务明确,容易保障及调试。
  6. M层要就对作业逻辑实现的包装,一般工作逻辑最多之是关乎到客户端和服务器之间的事体交互。M层要形成对应用网络协议和服务器之间的交互数据格式,本地缓存和数据库存储等富有事情细节的包,这些东西不可知暴露于C层,只让其提供一个接口
  7. C层的来意就是是拍卖视图的风波,然后调用业务逻辑,接收工作逻辑的处理结果通知,然后通知视图去刷新界面。
  8. M层是劳动者,负责数据的构建与翻新,C和V层负责数据的采取以及消费。
  9. C具有负责创建并负有M层对象的义务,C层也是一个施用观察者。
  10. C层负责M层的章程调用。
  11. C层负责相M层的数额变化通知并开展对应处理。

—— MVVM并非框架,而只是简单的文件夹分类 ——

view层的社和调用方案

MVVM被引入的前因后果

约莫是以2010年左右挪端支付火了起,起初是iOS,Android,
WinPhone三只好平台竞争,后来后者退出了争夺,变成了亚分开世界。从以系统布局及为开发者提供的框架体系来拘禁,两单阳台还是生产了经MVC三交汇组织的开发方式,这三重叠所代表的含义是范、视图、控制。这个开框架的初衷其实也不行粗略:视图负责展示同渲染,模型负责作业逻辑的实现,控制负责调度视图的波及工作逻辑的调用以及通报视图的基础代谢通知。
三有些松散耦合,各司其职。下面是藏的MVC框架结构:

[图表及传失败…(image-1d68cb-1512992093070)]

一个雅惋惜的实况是凭是Android和iOS都只是对C和V两局部进行了正规的概念跟落实:Android的视图部分的贯彻是概念了各种控件以及由此XML文件来组装视图布局界面,iOS的视图的实现吗是概念了各种控件以及经过XIB或SB来组装视图布局界面;
Android的操纵部分则是由此Activity来实现,而iOS的支配部分则是经过UIViewController来实现之。而模型有为?因为每个应用的事情逻辑与使用场景并不相同,所以个别单阳台吗无力回天为不克定义来一个通用的模型层出来,而是把模型层的定义留给了开发者来贯彻。然而当下吗咱的开发者在行使MVC框架开发使用时盖下了隐患。

初期的运开发相对简单,因为从没专业的模型层的概念,而控制层又以工程变更时留了过多只是供应开发者写代码的地方,所以众多开发人员就自然而然的拿事情逻辑、网络要、数据库操作、报文拼装和剖析等等全部代码都放入了控制层里面去矣,根本就是无待什么模型层的概念。
这样就年华的缓和以之错综复杂增加,就起了C层膨胀的场面了。一个控制器的代码可能出现了好几千执行之现象。于是乎有人就开始物色解决方案来吧C层瘦身了。又一个不行可惜的实况是尚从来不丁去想在抽象出M层,而是用了如下方法来解决问题:

  • 客户端和服务器之间交互的多少报文是否好定义有一个个只有属性而从不艺术的数据对象也?这样在拍卖与渲染界面时就是非需跟原之XML或者JSON或者其他的格式报文交互了,只要操作数据对象就哼了。于是解决方案就是是依据客户端和服务器之间交互报文定义来一个个底数据模型,然后再出有同效XML或者JSON和数据模型之间互转的解析器来。最后将立刻一个个仅仅生数量如果并未辙的目标数据模型统一置于一个地方,然后给他俩定义也M模型层(呼!终于被起模型层的概念了,但是:Are
    you kidding
    me??)
    。这样C层就不见面更出现XML或JSON解析及一直读取报文的代码了!而是把立即部分代码挪至范层了(大家来拘禁什么,我算以达到了MVC框架了!)
    好了!瘦身第一步成功。但是只是,问题尚以啊,我之事务逻辑还是一样大片在C层啊,看来MVC这种框架为可是这样啊!根本未曾解决本身的题材。不行,我非克再就此MVC这种框架来支付自己的用了,我若另外寻它套,要累本着C层瘦身。

  • 自家的某个界面和某某业务逻辑是绑定以同一块的,这个界面的来得是经过调用某个业务逻辑来贯彻之,业务逻辑完成后一旦一直更新是界面。这种环环相扣的调用和更新关系一向不怕非欲C层的与。因此好拿马上有界面的换代刷新以及事情逻辑的调用绑定在一齐,
    二者结合呢一个封而独自的总体并摇身一变独立的切近。这样将这看似的代码抽离出来了,存放到一个独的文书夹着。我管这片被什么好吗?对了不畏让视图模型层VM吧!视图模型层中之类定义了一个吃外部使用的唯一接口来供C层调用。这样我算将同十分片段代码从C层中抽离出来了。我既成之兑现了C层的愈加瘦身,并抽象出了一个视图模型层了!(不过哪里好像不对,视图模型层设计到了视图、模型、视图模型层三者的彼此与耦合)
    不过没有关联,反正我之C层进一步瘦身成功了!,我看看还只是免得以延续瘦身C层?

V和VM以及M之间的依赖关系

  • 本身之无数视图的事件是在C层中处理的,那我是休是好管C层的事件处理也拿出去啊?
    干脆就将出去吧。但是怎么用出来为?于是乎我又非停歇的摸,终于找到一个叫RAC的事物了,这个东西好啊,他好当处理视图的各种风波,以及可承担连续的网络调用。等等。。。
    RAC就是产生硌晦涩难知晓!难以学习,代码难以阅读和调剂。怎么惩罚?
    没有关联,只要是能用C层的代码瘦身这些又到底什么。。。大不了就是大半巡一点坑,多打几潮培训就好了。
    嗯! 就这样办,那自己管这有些代码也放入到VM层里面去吧。

    。。。。呼!!!
    C层终于瘦身成功。然后大家看呀,我之C层里面确实是啊代码也不曾了。。。
    它不再处理视图的波了,因为事件让RAC给处理了、它吧未处理视图的基础代谢和事务逻辑的调用了以于视图模型MV给处理掉了、他也未处理数据的分析了因为被范层为替换掉了。嗯。。。。我如果让这种没有C层或者未欲C层的框架起个名,叫什么好吧?
    就吃:MVVM吧。。。
    我之采用可毫无C层了,然后自己就奔走相告。将C层无用大白于天下。。

审是这样吧?答案是NO!!!

先是我思说的凡一个良好之框架中各层次之拆分并无是略的拿代码进行分类和细分,**层次的分开是横向的,而模块的分割则是纵向的
** 。
这之中提到到了层次中的耦合性和天职的分割,以及层及层里的互相接口定义和方法,同时层内的宏图也应负有莫大的内聚性和结构性。而这些规划之求并无当所谓的MVVM中反映出来。

  • 首先要正确的理解MVC中之M是什么?他是数据模型吗?答案是NO。他的不错定义是工作模型。也不怕是你具备工作数据与事务实现逻辑都应有定义在M层里面,而且工作逻辑的落实同概念应该跟现实的界面无关,也就算是同视图以及控制次莫另外的涉,它是好单独在的,您还是可将事情模型单独编译出一个静态库来提供被第三正或其它系统以。在地方经典MVC图中为深鲜明的描述了就或多或少:**
    控制负责调用模型,而模型则用处理结果发送通知被控制,控制再通报视图刷新。因此我们不克拿M简单的领悟吧一个个单调的只有属性而无办法的数据模型。其实这间涉及到一个最为核心的设计原则,那便是面向对象的中心计划标准:就是啊是看似?类应该是一个个颇具同等操作与见仁见智属性的对象的架空。
    我眷恋今天别一个系中都不曾起了一样积只来多少要并未艺术的数据模型的集结被定义为一个独自而空虚的模子层来供大家使用吧。**
    我们不克把一个封存数据模型的文书夹来作一个交汇,这并无入横向切分的平整。所以说MVVM里面的所谓对M层的定义就是是一个伪概念。

  • 方我早已说明M层是业务模型层而未数据模型层,业务模型层应该封装所有的工作逻辑的兑现,并且和具体视图无关。我们无可知将一个视图的呈现逻辑绑死在一个作业处理逻辑之中,因为发或有一个业务逻辑来多种不同的展现形式,也说不定界面显得会趁机以升级要生成,但是事情逻辑是相对稳定之。即使是某个视图确实就是和此事情是紧紧耦合的,也非该举行强耦合绑定。所以地方所谓的VM这种将视图的亮以及事情的拍卖逻辑绑定以平等块是老大不好的办法,因为这样的设计方式既全违背了系里头太基本的亮以及实现应有分别处理标准。而且这种计划之合计是与分支的观是违的。因为他出现了视图和事情的紧耦合和互双向依赖问题,以及与所谓的M层也如紧耦合的留存。所以说MVVM里面所谓的VM层的概念也是一个伪概念。所谓的VM层这其间只不过是以页面进行的效能拆分而已,根本不怕称不达所谓的层的概念。

  • 更来说说事件处理。经典的C层设计的目的是负担事件处理和调度,不论是按钮点击还是UITableview的delegate以及ListView的Adapter都太好放在C层来处理,这为是顺应C层最本色之概念:就是C层是一个担负调度和决定的模块,它是V层和M层的粘合剂,他的企图就是处理视图的轩然大波,然后调用业务逻辑,然后接收工作逻辑的处理结果通知,然后再通知视图去刷新界面,这就是是C层存在的意义。而且系统默认为是据这法子设计的。而RAC的产出则将马上一部分底处理让的的代表掉了。也就是是由此RAC所谓的响应式和触发式这种体制就算会落实将事件之调度处理在另地方其他时候还能够好。这样做的目的使得我们好疏散和释疑代码。但结果出现的题目呢?就是跟一个单元调度处理逻辑和效益的构建了在了一个地方,但不同的单元逻辑的以散于不同之地方,无法去分类统一保管和保护。因此而无法一下子即便理解有功能有所调度到底是怎落实和在哪里实现的。因为RAC将作用构建和事件处理完全粘合到一个不胜的函数体内部,并且是代码套代码的模式,这种措施严重的损坏了面向对象里面的构建与处理分离的设计模式理论。更麻烦的凡那高昂的习及护卫资产,代码阅读理解困难,以及无处不在的闭包使用。试想一下者对于一个新家的话是不是噩梦?,一旦有了问题对保护和代码调试是未是噩梦?而且使用不当就会面世循环引用的不得了问题。这样一来原本C层一个调度总管的职责被RAC来接管后,这些处理将移得分散和无序,当我们要开有联合之管住仍HOOK和AOP方面的物常常便易得力不从心下手了。
    不可否认的是RAC在拍卖连续调用以及各个响应者发生自然之优势。一个例证是咱们或有连日的大半只同服务器的网络要,这时候用RAC进行这种拍卖能便于的缓解问题。但是自思说的凡当在这种光景时,我们尤其应该用这种连的纱调用在M层内部消化掉,而单独受C层提供一个简易而便宜的接口,让C层向未需关爱这种调用的连续性。因此好说为把C层的代码给消化掉而引入RAC的机制,不仅没简化掉系统反而下跌了系统的可维护性和可读性。RAC机制向不怕无吻合用当事件处理中。美之动与框架并无在代码的数目,而是完全系统的代码简单好读,各部分任务分明,容易保障的调试

—— MVVM被引入的根本原因是对准M层的错误认识所引的 ——

M应该做的事:
给ViewController提供数据
给ViewController存储数据提供接口
提供经过抽象的业务基本组件,供Controller调度

C应该做的事:
管理View Container的生命周期
负责生成所有的View实例,并放入View Container
监听来自View与业务有关的事件,通过与Model的合作,来完成对应事件的业务。

V应该做的事:
响应与业务无关的事件,并因此引发动画效果,点击反馈(如果合适的话,尽量还是放在View去做)等。
界面元素表达

MVC中M层实现的律

说了那么多,可以总结出所谓的MVVM其实并无是如出一辙种所谓的框架或模式,他不过是一个非法框架而已,他只是用力量和处理按文件夹的措施展开了划分,最终之的结果是系统乱成了千篇一律锅粥。毫无层次可言,所兼有的绝无仅有亮点是把C层的代码和力量全弱化了。其实出现这种规划艺术极其根本的因由即从未针对性M层进行不易的知道定义跟拆分。那么我们应如何对的来定义及计划性M层呢?下面是本人个人认为的几个准则(也许与其他人的见发生出入):

  • 概念之M层中的代码应该与V层和C层完全无关之,也就算是M层的目标是匪需要靠任何C层和V层的靶子要独立有的。整个框架的统筹极端良好布局是V层不依赖C层而单身在,M层不依赖C层和V层独立有,C层负责涉二吧,V层只承担展示,M层持有数量及业务的具体落实,而C层则处理事件响应与业务的调用以及通知界面更新。三者之间自然要显著的概念为才为依靠,而非该出现双向依赖。下面是三重合的指关系图:

其三交汇里的就为依靠关系

惟有当你系统规划之异部分都是一味为依靠时,才可能好之拓层次拆分和每个层的作用独立替换。

  • M层要就对业务逻辑实现的包装,一般工作逻辑最多之是涉及到客户端和服务器之间的事体交互。M层里面要做到对下的网络协议(HTTP,
    TCP,其他)、和服务器之间交互的数码格式(XML,
    JSON,其他)、本地缓存和数据库存储(COREDATA,
    SQLITE,其他)等有着业务细节之包裹,而且这些事物还无能够暴露于C层。所有供C层调用的且是M层里面一个个业务类似所提供的成员方法来贯彻。也就是说C层是匪欲明白为无应有清楚与客户端以及服务器通信所下的其他协议,以及数报文格式,以及存储方的情节。这样的功利是客户端和服务器之间的通信协议,数据格式,以及地面存储的转还不见面影响其它的使用整体框架,因为提供给C层的接口不更换,只需要提升跟换代M层的代码就得了。比如说我们怀念以网络要求库从ASI换成AFN就如以M层变化就是可以了,整个C层和V层的代码不转移。下面是M层内部层次的定义图:

M层内部的包裹层次

  • 既然我们的采取是一个完全而与此同时分开模块,那么业务层内部也应有遵照功能模块进行组织划分,而休应该简单且平面的按照和服务器之间通信的接口来进展作业层次的面封装。我深信来众多总人口且是指向M层的包就是简单的依同服务器之间的互接口来概括的包装。下面的简单栽不同之M层实现之业务封装方式:

简单种不同之M层封装实现

我们尚可以更进一步的针对工作逻辑抽象出M层的接口和贯彻两有些,这样的一个功利是一致的接口可以生例外之落实方式,以及M层可以隐蔽非常多的中间数据与章程而非露于调用者知道。通过接口和贯彻分离我们尚好以无移原先实现之基本功及,重新重构业务有的兑现,同时这种模式吗充分爱MOCK一个测试实现,这样于进展调试时方可死粗略的在真实实现和MOCK实现中切换,而无需每次都同劳务器端进行互调试,从而实现客户端和服务器之间的个别支付暨调剂。下面是一个升迁版本的M层体系布局:

据悉接口的M层实现

  • M层如何跟C层交互的问题吧需要考虑,因为M层是勿欲明白C层和V层的存的,那么M层在事情处理了后安去通知C层呢?方法发生良多种:
    • 咱们好啊M层的通逻辑定义Delegate协议,然后于C层去贯彻这些协议,然后M层提供一个delegate属性来赋值处理事务通知的对象。
    • 俺们为得定义众多的NSNotification或者波总线,然后当M层的事务处理了后得以发送通知,并且在C层实现通之处理逻辑。
    • 咱得以据此闭包回调或者接口匿名实现目标的款式来落实工作逻辑完成的关照功能。而且可定义有正规:所有M层对象的法门的最终一个参数都是一个正式的如下格式的block或者接口回调:

typedef void (^UICallback)(id obj, NSError * error);

这种模式其实当许多系遭到起动及。大家可以参数考苹果的CoreLocation.framework中之地理位置反解析的类CLGeocoder的概念。还有某些底凡在AFN以及ASI中之网络要部分都是管成功和黄的处理分成了2个block回调,但是这里建议在叫C层的异步通知回调里面未分2独block来调用,而是一个block用2个参数来解决。因为生或我们的拍卖着无成还是败诉且可能发生部分代码是相似的,如果分别则会起还代码的问题。

delegate这种模式并无局限为M和C之间,同样为堪采取在V和C之间。

delegate本质实际上是同等种双方间通信的接口,而通过接口来进展通信则可尽要命限度的减对象中的耦合。

MVC中M层实现的简要举例

末段咱们因为一个大概的用户体系的登录系统来贯彻一个M层。

1.定义标准的M层异步回调接口:

//定义标准的C层回调block。这里面的obj会根据不同对象的方法的返回而有差异。
typedef void (^UICallback)(id obj, NSError * error);

//这里定义标准的数据解析block,这个block供M层内部解析用,不对外暴露
typedef id (^DataParse)(id retData, NSError * error);

2.概念有M层业务类的基类,这样以通用基类里面我们好举行过多拍卖。比如网络层的合并调用,加解密,压缩解压缩,我们尚可以做AOP和HOOK方面的拍卖。

     @interface  ModelBase

           //定义一个停止请求的方法
           -(void) stopRequest;
           /**
             *定义一个网络请求的唯一入口方法
             * url 请求的URL
             * inParam: 入参
             * outParse: 返回数据解析block,由派生类实现
             * callback: C层通知block
             */
           -(void) startRequest:(NSString*)url  inParam:(id)inParam outParse:(DataParse)outParse  callback:(UICallback)callback;
     @end

3.概念一个用户类:

    @interface  ModelUser:ModelBase

        @property(readonly) BOOL isLogin;
        @property(readonly) NSString *name;

       //定义登录方法,注意这个登录方法的实现内部可能会连续做N个网络请求,但是我们要求都在login方法内部处理,而不暴露给C层。
       -(void)login:(NSString*)name  password:(NSString*)password   callback:(UICallback)callback;
        //定义退出登录方法
       -(void)logout:(UICallback)callback;
    @end

4.定义一个M层总体系统类(可选),这个仿佛可是单例对象:

    @interface ModelSystem:ModelBase

     +(ModelSystem*)sharedInstance;

    //聚合用户对象,注意这里是readonly的,也就是C层是不能直接修改用户对象,这样保证了安全,也表明了C层对用户对象的使用权限。
    @property(readonly)  ModelUser *user;  

    //定义其他聚合的模块

    @end

5.于C层调用用户登录:

  @implementation LoginViewController

    -(IBAction)handleLogin:(UIButton*)sender
   {
        sender.userInteractionEnabled = NO;
        __weak LoginViewController  *weakSelf = self;
       [[ModelSystem sharedInstance].user  login:@"aaa" password:@"bbb"  callback:^(ModelUser *user, NSError *error){

        if (weakSelf == nil)
               return;
       sender.userInteractionEnabled = YES;
       if (error == nil)
       {
              //登录成功,页面跳转
       }
       else
      {
            //显示error的错误信息。。
      }}];

   }

   @end

好看来地方的C层的组成部分非常简单明了,代码也易读和易于掌握。同时我们尚看到了C层跟本不需要懂得M层的报到实现到底是安告网络的,以及要了几个大网操作,以及用的什么协议,以及什么数据报文格式,所有的即刻所有都封闭装在了M层内部贯彻了。C层所而做的便是大概的调用M层所提供的方式,然后在callback中通界面更新即可。整个C层的逻辑吗就是是几十执行就能行定了。

切切实实的模型层设计方式要参考:M层的宏图


迎接大家关注本身之github地址,关注欧阳大哥2013,关注自我的简书地址:http://www.jianshu.com/u/3c9287519f58

除delegate外也堪运用Block异步通知方式

typedef void (^BlockHandler)(id obj, NSError *error);

运用block方式定义异步方法时相似要吻合如下几个规则:

  1. BlockHandler的参数确保就是永恒的2独:一个是异步方法返回的对象,这个目标足以依据不同之主意而回不同的靶子。一个NSError对象表示异步访问有了错误的回。
  2. block回调处理作艺术的结尾一个参数。
  3. 匪建议以一个计中出现个别只block回调:一个科学的及一个垮的。
  4. 推介:对于在M层对象吃
    某个请求通过block回调来通知调用者进行异步更新的机制。一个尺码,只要提到到M层对象的主意调用都尽心尽力走正规block回调这种办法。例如:

- (void)fn1:(参数类型)参数 callback:(BlockHandler)callback;
- (void)fn2:(参数类型)参数 callback:(BlockHandler)callback;
- (void)fn4:(参数类型)参数 callback:(BlockHandler)callback;
- (void)fn5:(参数类型)参数 callback:(BlockHandler)callback;

点的道实现和调用机制看起还挺统一以是基准。即使有方法并无其他异步动作,也极好遵守这种模式,因为某天有或会见由一道实现成为异步实现。这样的话只待改变C层的代码即可。

delegate异步通知以及Block异步通知之抉择参考:

  • 如若有类吃享有许多单法子,而每个方法以实现了不同的功力,并且方法的异步返回数据以及斯点子有老强的关联性就应当考虑下block而不是delegate
  • 要是类似吃的不二法门的异步方法是那种一涂鸦相就收获一个不同的结果,而且得的结果与上次结果没什么关联,就考虑下block而不是delegate
  • 要我们调整用类似中之某个方法,而调用前设置了有上下文,而调用方法后我们以欲根据此上下文来处理的归结果时,就考虑采取block而不是delegate
  • 一经我们调用类里面的某个方法,而回到的结果未待跟上下文进行关联那么就算考虑以Delegate而不用block
  • 假若一旦实时的观赛业务类里面的之一属性之变迁时,我们即便应该考虑采用Delegate假如休是运block
  • 假设业务类里面的异步通知或者分为好几单步骤那尽管考虑以Delegate要是未是使block

哟状况下用KVO的计贯彻异步通知回调:

  1. 某个对象的平等数据更新可能会见招多独依靠是目标的靶子的换代变更处理。
  2. 设某对象的生命周期要较观察者短则无建议就此KVO的点子,因为是来或会见导致系统的倒而造成巨大的震慑。

delegate和block的缺点

  • delegate的法必须要优先定义来一个接口协议来,并且调用者和实现者都得依照这接口规则来进展通报以及数目处理相互,这样平空就发生了必然之耦合性,也即是二者之间还是拥有隐式的借助。不便民扩展和进行完全由定义处理。
  • block的缺点:
    1. 凡运不好会产生循环引用的问题
    2. 差后麻烦调试以及难以开展问题追踪
    3. 代码中起多又嵌套,从而影响代码的好看与可读性

KVO机制的助益

作业对象和观察者之间全剥离了耦合性,而且数量变化后底通了由网来拍卖,不需要加上附加的代码和逻辑,而且得兑现多观察者而监听一客数据。

症结:只能对性能的更动进行观察。

NSNotification缺点

这种机制过于松散,没有涉嫌的上下文。给使用者带来了迟早之学习成本。

总结

每当设计一个业务层时,首先应当要针对性工作进行精心的辨析与喻,然后构建起一个看似组织图,这种静态框架设计好下,就待对类进行角色跟任务分开,哪些应该设计为数模型类,哪些该设计也罢业务类。

相关文章

admin

网站地图xml地图