美文网首页
以逻辑为中心,还是以显示为中心?这是个问题

以逻辑为中心,还是以显示为中心?这是个问题

作者: 珞泽珈群 | 来源:发表于2020-02-29 22:42 被阅读0次

    前言

    一提起Android架构你想到了什么?说来说去还是MVC、MVP、MVVM,什么,你说还有Clean,就其实现而言,那只是细分Model下的MVP。这些架构都有一个什么特点呢?显而易见的是它们都以MV开头,所以有时候也统称为MVX架构。更深层次的共同点是,它们都是围绕着View来的,都是研究如何把Model数据输送给View来显示的,也就是以显示为中心。然而一个现实的问题是,如果安置那些与显示不直接相关的逻辑(我说的不是那些后台服务的逻辑,那些跟显示完全不相关)?手机的形态决定了显示总是在一屏一屏之间来回切换,与之相应的业务逻辑也往往是与显示绑定的,随之一同切换的,这是非常自然且合理的行为,毕竟看不见的东西就没必要耗费资源。然而总有一些逻辑是跨显示/屏幕而存在的,这些逻辑放在哪里合适呢?

    MVX的解决方案

    View层肯定没戏,剩下的就是M层和X层。还是以MVVM为例吧,解决的方案只能是Model层和ViewModel层。
    首先是ViewModel层,与显示不直接相关的逻辑和跨显示的逻辑可以放在ViewModel中,尤其是后者,Jetpack ViewModel文档中专门有一项在Fragment之间共享数据,其实就是通过Activity共享同一个ViewModel,自然就可以共享数据,也可以共享跨屏幕逻辑。但是这个方案有个问题,就是只能通过共同的Activity共享逻辑,这个层次实在是太单薄了。Fragment自己的逻辑放在自己的ViewModel中,与兄弟Fragment共同的逻辑放在Activity的“父ViewModel”中,那“父ViewModel”们更加共同的逻辑呢?没方法放了,拢共只有两层的逻辑层次实在是太单薄了。不过这种方案的优势就在于简单直接,对于不太复杂的应用来说也就足够了。
    Model层当然也可以担此重任。通常情况下我们认为Model层是一层,就实现而言,Model层一般是一个单例的Repository类(或者其它什么名字),Model层承担的职责就是从不同的数据源存取数据,屏蔽各个数据源之间的差异,ViewModel只要发过来请求需要什么样的数据就可以了,不必在意这些数据是从哪里来的,所以把Model层以一个单例的形式呈现是非常合理的。然而这只是一般情况,或者说是简单情况,我们当然可以把Model层实现为复杂的多层的结构,就像这样:

    如果Model层像上面实现的那样,那共享逻辑就变得很简单了,ViewModel只需要去获取对应的一个或多个Repository,然后该咋地咋地就行了,毕竟共享的逻辑都已经安放在对应的Repository中了。正如上图所示,Model层的层次可以是任意的树状结构,共享的逻辑也可以是任意多层,不会像通过ViewModel共享那样只有单薄的两层。
    然而这只是理论上的可行性,这里面有个巨大的问题就是Model层树状结构的构建不是一蹴而就的,我们必然不会在应用开始时就构建出完整的树状Model层,有些Repository只有在需要的时候才有必要创建,然后添加到树状结构中,有Repository的创建就有Repository的销毁,当用户彻底离开某项业务逻辑时,对应的Repository及其所有子Repository都需要销毁。那什么是有必要的时候,什么是离开的时候呢?你可以说是进入或者离开某个特定界面的时候,但是现实没有这么简单,既然这样的Repository是为了共享某种逻辑,那么往往并不是进入或者离开某个界面才决定了某个Repository的创建或销毁,而是进入或者离开某个逻辑单元时。所以说让View决定Repository的结构并不合适。

    虽说上面两种方案都各自的问题,但是对于大部分应用来说还是足够的,可以把ViewModel和Model结合起来来共享逻辑,对于那些简单的数据共享而言,通过共同的ViewModel就可以了,对于稍复杂的需要更多层次的共享逻辑而言,通过在Model层建立个两三层的树状Repository也还是可行的。

    以逻辑为中心

    MVX架构都是以View为中心的,为View配一个Presenter,为View配一个ViewModel等,这就导致那些跨View甚至是无View的逻辑不好安放。事实上,View本应该是业务逻辑的附属物,逻辑才应该处于核心的位置,逻辑驱动View的变化,逻辑也可以独立于View而存在,而不应该反过来。那么有没有以逻辑为中心的架构呢?当然有,Uber开源的RIBs就是其中的一个代表,RIBs是VIPER架构的一个变种,这种架构在Android这边寂寂无名,它是iOS中架构的一种(也不是很常用),Uber借鉴了VIPER的思想,把它改造成了RIBs,值得一说的是,RIBs是一个跨平台架构,在Android和iOS上有各自对应的实现,Uber用它来统一手机端的架构。但是,可以这么说,RIBs架构不适用于绝大部分应用,它是为复杂业务逻辑设计的一种架构,非常适合于Uber这种,10%的界面可能承担了90%的业务逻辑,业务逻辑很复杂,层次非常多的情况。以下也不会介绍RIBs的具体使用方式等内容,只会从比较高的层次上去看如何实现以逻辑为中心的架构。

    RIBs

    啥是RIBs,RIB是Router、Interactor、Builder的缩写,RIBs就是很多RIB的意思,可见Router、Interactor、Builder是这个架构的核心,当然也还有一些别的辅助类Presenter、View和Component。它们之间的关系是这样的:

    在RIBs中每个业务逻辑单元都包含有一套Router(路由)、Interactor(业务逻辑)、Builder(构建者),可以没有View和Presenter,而Component其实就是Dagger中的那个Component,它是包含在Builder中的,用于构建别的类。

    • Router:正如它的名字一样,起路由的作用,也就是从一个界面跳转到另一个界面的。一般而言,界面的跳转都是View层的责任,但是如果是这样的话,那就成了以View为中心了,View发生了变化,然后跟着配套的ViewModel等来了。但是在以业务逻辑为中心的RIBs架构中不是这样的,是逻辑发生变化了,然后View改变了,这种方向上的转变正体现了以逻辑为中心的思想。虽然理论上讲,Interacter是RIB的核心,但是在实现上,Router处于中心的位置,因为Router不仅包含跳转的逻辑,Router内还包含Interacter,View,Component和其子逻辑单元的Builder,也就是说,拿到Router就能构建一个逻辑单元的一切。Router是怎么实现跳转的呢?实际上就是通过子逻辑单元的Builder创建子Router,然后添加到自己的children中,并且把子View添加到自己的View中就OK了。如果是“返回”,就是反回来,从中删除。
    • Builder, Component:构建一切,每个Component都有一个父Component,形成了Component的树状依赖结构。Builder会创建出Router,如上所说,Router其实包含了一切。
    • Interacter:业务逻辑,可以是纯逻辑,因为其可以完全脱离View而存在。
    • Presenter, View:可选项,View就是Android中的View,Presenter是个接口,用于沟通Interacter和View,所以Presenter一般包含两种方法,一是可供Interacter调用的View的显示方法,二是Interacter可以响应的View的事件,事件以RxJava流的形式体现。

    简单描述一下整个流程就是,首先Activity创建一个RootRouter,RootRouter包含RootInteractor,Activity的容器ContentView,所有子逻辑单元的Builder,而RootInteractor会在一开始的时候就会通知RootRouter添加首页的Router,RootRouter通过子逻辑单元(这里就是首页)的Builder创建出首页的Router(又包含Interactor等那一套东西),然后添加到RootRouter的children中,首页View添加到ContentView中,这样一个界面就显示出来了。之后首页有这样那样的操作,首页的Interactor会响应,进行业务逻辑,然后通知首页的Router添加或者删除子逻辑单元,或者首页Interactor处理不了,它会调用父Interactor(也就是RootInteractor)的方法,交由父Interactor处理,父Interactor可以删除首页Router及其View,添加别的子Router和View,这样就完成了跳转。整个应用就是在一个个Router的添加和删除中不断变化的。像是这样:

    RIB树的变化

    RIBs架构是如此的“别致”,我们经常说View Tree,但是RIBs却在构建一个Business Logic Tree,Business Logic Tree的变化影响了View Tree的变化,而不是相反,逻辑核心(Router,Interactor)先构建好,然后决定View的变化,甚至可以只有逻辑核心而没有View,这丝毫不影响什么。每个Interactor都活在自己的世界里,不知有汉无论魏晋,自己能处理的就处理(自己只能决定自己的直接子逻辑单元的添加和删除),不能处理的就交由父Interactor。View事件的暴露是靠RxJava数据流(RxBinding),每一个Interactor的数据暴露也都是RxJava数据流,这样父Interactor的数据就可以传递到子Interactor中了。RIBs还有很多特性,这里就不一一说了。
    RIBs的缺点也很明显,那就是太复杂,即使一个简单的界面也得构造一大堆东西(Uber也开发了工具自动创建了一些模板代码来减轻负担),显然这是为复杂业务逻辑设计的架构。RIBs非常重的依赖于Dagger和RxJava,能用Dagger构造出这么复杂的树状依赖结构,真是让我大开眼界。还有就是社区资源也很匮乏。总之这不是一套会流行的架构,但是其别具一格的思想还是让我眼前一亮。

    以逻辑为中心,还是以显示为中心?

    这是个问题。对于稍微复杂一点的应用,理论上以逻辑为中心是更加合适的选择,因为复杂的应用往往是逻辑层次的复杂,而不是界面的复杂,但从实践的角度讲,从以显示为中心到以逻辑为中心的切换是翻天覆地的,变化太大反而就没得选择了。我还没有发现一种以逻辑为中心的,适合于一般复杂逻辑的简单架构。想要结合MVX和RIBs的优点,也不是一件容易的事,因为它们是如此的不同,以至于不好融合。So,开阔眼界,保持思考,就这样。

    相关文章

      网友评论

          本文标题:以逻辑为中心,还是以显示为中心?这是个问题

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