Flutter组件化和平台化架构

4,886 阅读5分钟

什么是组件化/平台化

在App开发和架构设计时, 考虑如何去管理代码复用, 解耦

如何将功能进行分治,将大问题分解成多个独立的小问题。

组件化和平台化就是App开发中最流行的分治手段。

本节不仅适用于Flutter架构设计,也同样适用于Android/iOS等一切软件开发

组件化

组件化又叫模块化, 将一个大型App按照关注点分离拆分成多个独立的组件或模块。

每个独立的组件都是一个单独的系统,可以单独维护、升级甚至直接替换,也可以依赖于别的组件,

只要组件功能不发生变化,就不会影响其他组件和APP的整体功能。

组件化的中心思想是将独立的功能进行拆分。

组件可以是一个软件包(Package)、页面、UI控件,甚至可能是封装了一些函数的模块。

组件代码如何组织, 需要遵循如下基本原则

单一职责

每个组件仅提供一个功能,有自己固定的职责清晰的边界, 这样才能良性发展。

一个反例是Common或Util组件:“这段代码放哪儿好像都不合适,那就放Common(Util)吧”。定义不明确、归属边界不清晰的代码, 最后变成了垃圾堆。

再遇到不知道该放哪儿的代码时,就需要重新思考组件的设计和职责了。

接口稳定原则(开闭原则)

  • 接口应该具有高复用度并保持稳定。要做到这一点,在组件封装时做好功能抽象接口设计
  • 所有可能发生变化的因子都在组件内部做好适配,不要暴露给它的调用方。

稳定性原则

不要让稳定的组件依赖不稳定的组件。

比如组件1依赖了组件5,如果组件1很稳定,但是组件5经常变化,那么组件1也就会变得不稳定了,需要经常适配。

如果组件5里确实有组件1不可或缺的代码,我们可以考虑把这段代码拆出来单独做成一个新的组件,或是直接在组件1中拷贝一份依赖的代码

减少依赖

组件尽量减少对其他底层组件的依赖。比如,直接拷贝依赖的代码到组件中, 删掉依赖组件。

组件化具体实施步骤

  1. 剥离与业务无关的基础功能,比如网络请求、组件中间件、第三方库封装、UI组件等,将它们封装为基础库

  2. 按照业务维度,比如首页、详情页、搜索页等,去拆分独立的模块了。

    拆分的粒度可以先粗后细,只要能将大体划分清晰的业务组件进行拆分,后续就可以通过分布迭代、局部微调,最终实现整个业务项目的组件化。

  3. 按照上面的4个原则,修正各个组件的依赖,以及最小化对外的接口。

组件循环依赖

如果组件之间的依赖关系比较复杂,就会在出现功能耦合现象。 如上图所示,组件2和组件3同时被多个业务组件和基础功能组件直接引用,甚至组件2和组件5、组件3和组件4之间还存在着循环依赖的情况。

一旦这些组件的内部实现和外部接口发生变化,整个App就会陷入不稳定的状态,即所谓牵一发而动全身。

平台化

平台化是组件化的升级,即在组件化的基础上,对它们提供的功能进行分类,统一分层划分,增加依赖治理的概念。

我们按照四象限分析法,把App的组件按照业务和UI分解为4个维度,来分析组件可以分为哪几类。 经过分解后,组件分成4类

  • 具备UI属性的独立业务模块;
  • 不具备UI属性的基础业务功能;
  • 不具备业务属性的UI控件
  • 不具备业务属性的基础功能

平台化分层

平台化与组件化最大的差异在于增加了分层的概念,每一层的功能均基于同层和下层的功能之上,

组件之间既保持了独立性,同时也具有一定的弹性,在不越界的情况下按照功能划分各司其职。

平台化关注的是组件之间关系的合理性,遵循单向依赖原则。

  • 单向依赖原则,指的是组件依赖的顺序从上到下依赖,不要出现下层模块依赖上层模块这样循环依赖的现象, 避免复杂的耦合。

  • 除了不允许出现下层组件依赖上层组件的情况,跨层组件和同层组件之间的依赖关系也应当严格控制。

下层组件怎么调用上层组件

增加中间层,比如Event Bus、Provider或Router,以中间层转发的形式实现信息同步。

比如,位于第4层的网络引擎中,会针对特定的错误码跳转到位于第1层的统一错误页,这时我们就可以利用Router提供的命名路由跳转,在不感知错误页的实现情况下来完成。

又比如,位于第2层的账号组件中,会在用户登入登出时主动刷新位于第1层的首页和我的页面,这时我们就可以利用Event Bus来触发账号切换事件,在不需要获取页面实例的情况下通知它们更新界面。

Flutter系统分层

除了上面介绍的业务和UI四象限法则之外,也可以使用其他的划分策略,只要整体结构层次清晰明确,不存在难以确定归属的组件就可以了。

比如,Flutter就采用Embedder(操作系统适配层)、Engine(渲染引擎及Dart VM层)和Framework(UI SDK层)整体三层的划分。Flutter框架每一层的组件定义都有着明确的边界,其向上提供的功能和向下依赖的能力也非常明确。