组件化小结

在组件化之前,App 都是在一个工程里开发的,开发人员也比较少,业务发展也不是非常快,项目中不引用组件化开发也是可以的。但是当开发人员越来越多,业务越来越复杂,代码量也就越来越多,这时候单一的开发模式会显露出一些不足:

  • 耦合代码严重,代码没有明确的约束,逐渐臃肿
  • 开发效率不高,难以维护,维护成本过高

为了解决这些问题,于是提出了组件化开发的策略,拥有以下好处:

  • 方便针对性的模块化测试
  • 研发人员维护不同的模块,提高开发效率,降低维护成本

目前组件化开发的主流方式有三种:

  • URL-Block,
  • Protocol-Class
  • Target-Action

URL-Block

蘑菇街通过 MGJRouter 实现中间层,通过 MGJRouter 进行组件间的消息转发。

实现方式大致是,在提供服务的组件中提取注册 block,然后在调用方组件中通过 URL 调用 block

image-20201006022159755

MGJRouter是一个单例对象,在其内部维护着一个「URL -> block」格式的注册表,通过这个注册表来保存服务方注册的block,以及使调用方可以通过URL映射出block,并通过MGJRouter对服务方发起调用

在服务方组件中都对外提供一个接口类,在接口类内部实现block的注册工作,以及block对外提供服务的代码实现。每一个block都对应着一个URL,调用方可以通过URL对block发起调用。

不足

在蘑菇街组件化架构中,存在很多硬编码的 URL 和参数。在实际开发过程中,URL 编写出错会导致调用失败,而且参数是一个字典类型,调用方不知道服务方需要哪些参数,这些都是问题

Protocol-Class

image-20201006024413874

为了解决 MGJRouter 方案中URL硬编码,以及字典参数类型不明确问题,蘑菇街在原有组件化方案的基础上推出了 Protocol 方案。

Protocol 方案由两部分组成,进行组件间通信的 ModuleManager 类以及 MGJComponentProtocol 协议类。

通过中间件 ModuleManager 进行消息的调用转发,在 ModuleManager 内部维护一张映射表,映射表由之前的「URL -> block」变成「Protocol -> Class」

在中间件中创建 MGJComponentProtocol 文件,服务方组件将可以用来调用的方法都定义在 Protocol 中,将所有服务方的 Protocol 都分别定义到 MGJComponentProtocol 文件中,如果协议比较多也可以分开几个文件定义。

这样所有调用方依然是只依赖中间件,不需要依赖除中间件之外的其他组件。

Protocol方案中每个组件也需要一个接口类,此类负责实现当前组件对应的协议方法,也就是对外提供服务的实现。

程序开始运行时将自身的Class注册到ModuleManager中,并将Protocol反射出字符串当做key。这个注册过程和MGJRouter是类似的,都需要提前注册服务

Target-Action

CTMediator组件化方案分为两种调用方式:

  • 远程调用:通过AppDelegate代理方法传递到当前应用后,调用远程接口并在内部做一些处理,处理完成后会在远程接口内部调用本地接口,以实现本地调用为远程调用服务
  • 本地调用:由performTarget:action:params:方法负责,但调用方一般不直接调用performTarget:方法

CTMediator会对外提供明确参数和方法名的方法,在方法内部调用performTarget:方法和参数的转换

image-20201006025143422

架构设计思路

通过CTMediator类实现组件化的,在此类中对外提供明确参数类型的接口,接口内部通过performTarget方法调用服务方组件的Target、Action。

由于CTMediator类的调用是通过runtime主动发现服务的,所以服务方对此类是完全解耦的。

但如果CTMediator类对外提供的方法都放在此类中,将会对CTMediator造成极大的负担和代码量。

解决方法就是对每个服务方组件创建一个CTMediator的Category,并将对服务方的performTarget调用放在对应的Category中,这些Category都属于CTMediator中间件,从而实现了感官上的接口分离。

image-20201006025309633

实现细节

对于服务方的组件来说,每个组件都提供一个或多个Target类,在Target类中声明Action方法。Target类是当前组件对外提供的一个服务类,Target将当前组件中所有的服务都定义在里面,CTMediator通过runtime主动发现服务

在Target中的所有Action方法,都只有一个字典参数,所以可以传递的参数很灵活,这也是作者casatwy提出的去Model化的概念

在Action的方法实现中,对传进来的字典参数进行解析,再调用组件内部的类和方法。