热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

客户端模块化解耦实践Router

客户端模块化解耦实践–Router背景随着业务进入快速发展期,业务线拓展迅速,项目结构变得庞大复杂,导致迭代的成本越来越高。项目的越发庞大也使得整个工程编译时间越来越长。进行项目拆
客户端模块化解耦实践 – Router

背景

随着业务进入快速发展期,业务线拓展迅速,项目结构变得庞大复杂,导致迭代的成本越来越高。项目的越发庞大也使得整个工程编译时间越来越长。进行项目拆分后模块化运行(项目自运行、自管理)是一个较好的出路,但项目间的复杂相互引用导致我们无从下手,而如何去除这些项目间的引用则是今天的主题

产生

首先我们可以整理下现状,我们的碰到的最大问题其实就是项目间杂乱无章的耦合、引用

《客户端模块化解耦实践 - Router》 actu-structor

针对这些耦合、依赖,我们可以简单的分析:从理论上来说,每个项目负责单独的业务,应该本身就是相互独立的存在。但是实际上,处于同一个公司业务体系中,项目与项目之间不可能真正做到完全的切割。(公司与公司间都可能存在合作,项目与项目间当然就更可能了)

看来想从业务源头想去除业务依赖是不太可能了,我们只能从我们技术角度去进行项目间耦合、引用的去除了。而其中最关键的一点就是:耦合、相互依赖 , 而Router的概念正是为了解决这些问题而提出的。

那Router到底是什么呢?

Router是一个具有收口分发思想的、用于处理模块(项目)间直接依赖、调用的模型。最直接的体现其实就是一个约定、一个规则,按照约定规则进行解析,而后分发。

《客户端模块化解耦实践 - Router》 hope-router

发展

针对以上的一些问题,其实我们能get到一些关键特性,收口分发

OK,既然关键信息已经得到,伪代码很快就能敲出:

public static void jump(String rule){
if(rule){
gotoBusinessActivity()
return;
} if(rule){
invokeBusinessFunction();
return;
}
}

Perfect!!! 调用方便,确实做到了 收口分发

但是随着业务发展,弊端显示的也越发明显。仔细分析过后,可以总结出一些问题

膨胀

  • 随着业务的增长,膨胀的太厉害,成千上万行代码坐落在这个类中,名副其实的大杂烩

维护成本

  • 所有项目均同时维护,一个项目改错可能导致其他项目也受到影响
  • if-else的逻辑维护太复杂
  • 分发逻辑是 hardcode ,一旦错误将无法进行修改
  • 各项目无法真正分离

扩展

  • 从代码结构不难看出,这里的分发实质上是‘亲力亲为’并非真正的分发,而‘亲力亲为’是做不到真正的拆分的

从上面几个问题来看,其实收口并不难,如何做好分发这才是一件难事。

回顾需求与现有的问题,觉得自己的if-else模型其实丢了一件事:没有抽离出一套统一的流程

那流程怎么定义?

我在设计的时候喜欢类比现实生活,而Router也不外乎。而这个模型就很有趣了

我想要寄快递

这个是我需求,那么该怎么办呢?最重要的两点

  1. 地址
  2. 包裹

我只需要给到快递员地址以及包裹,那么就会给我送到(除非地址错了),但是这里要强调一点,我寄快递,并不是快递员来接受这个包裹。快递员只是,而真正接收的是所在地址的人。

想到这里其实就可以梳理出Router的核心生命周期与非核心生命周期了。当Router把调用者的意图转达给接受者时就已经完成了它的周期. 接下来的事就是接受者干的了,跟Router无关

《客户端模块化解耦实践 - Router》 invoke

到这里其实Router的基本框架已经确定(因为流程已经抽离), 那么剩下的就是具体模块的事了。不难分析出Router的两大核心模块:规则处理执行器

规则

通过模型,我们知道了两个必须的东西:地址包裹 ,那对应到程序中无非就是 目标指向数据 ,再契合到我们当前的业务场景,规则不难提炼出来:

protocol://path?data

是不是很眼熟?其实就是我们通用url的简化版本。其实重点就三个信息:

  1. 协议,用于版本区分、功能区分等
  2. 目标指向,路径,意图的接受者
  3. 数据,携带的数据

同样我们的规则定义为

tctclient://project/module?key=value

协议定义完毕了,但是难点来了: Path的映射如何处理

举个例子,Pathhotel/list ,但是这个只是一个字符串,我真正想要调用的是 酒店列表 HotelListAction ,如何将 Path目标指向 进行关联,这是一个问题。在讨论的过程中,其实有三种方案:

  1. if-else 直接逻辑关联
  2. 使用 Map 进行关联
  3. 使用 xml 进行关联

其实从本质上来说,都一样,都是为了数据关联。但是在维护、扩展上是有区别的。

if-else 这个不用说,直接的引用会产生的非常复杂的依赖网,而且无法抽离出分发这块,所有的分发都需要在当前环境下进行关联。

Map & XML的逻辑很像,都是 path处理类 的对应。但是在维护和扩展性上我们最终选择了xml, 优点很明显:无代码耦合可动态更新 。而MAP的优点在于,不需要进行数据加载,节省了这一部分的性能。

执行器

执行器的概念就好理解多了,当一段规则被解析成可读的意图后,我需要将意图、数据传递给接受者,而这个接受者可能是各个业务所处理的。因此我需要设计一个接口(不然没指向性了)来进行接收,而业务部门可通过实现这个接口来进行处理相关逻辑。

《客户端模块化解耦实践 - Router》 flow1

虽说到这里流程基本已经结束,但是在实际的业务逻辑中会衍生出一些特殊的逻辑,比如:

我要打开酒店列表,但是有个要求,必须是登陆状态才可直接进入,否则需要先去登陆。

可能仅仅从这一个需求上来看,用现在的流程去完成也不难,可以使用两个接收者去完成。但是当中间衍生的特殊逻辑很多而且可能多个业务都会需要,那用两个(多个)接收者可能就有会膨胀的很厉害了。那这个时候其实另外一个概念就产生了 — 拦截器

拦截器

拦截器是当一个规则产生最终作用前进行的一些特殊而通用的逻辑处理。

处理逻辑其实很像View Touch事件,当一个Touch事件产生后,首先是分配(分发),然后是拦截,最后才是消费

而我们的拦截器稍有不同的地方是

  1. 拦截器可以多个
  2. 只有当所有拦截器全部满足(pass)的情况下才会走到执行器(消费)
  3. 这种拦截器模型可以通过递归来进行实现

《客户端模块化解耦实践 - Router》 flow-interceptor

拓展

对于Router框架设计到使用到现在已经2年多了,业务需求等也都基本可以完成、满足。也确实做到了Router框架抽离、流程确定且业务逻辑(包括映射关系)各自维护。当一个业务需求产生时,只需要产生一个新的规则来维护即可。

但是重新审视下目前的框架其实仍然有很多需要完善的:

  1. 数据映射是通过xml来映射,那么必然会带来I/O加载的性能开销
  2. 是否项目调用需要写一段很冗长的规则,比如 jump("tctclient://hotel/list"),而这种字符串参数通常是无法通过编译器去检查正确与否的

针对这两个问题,其实我们采用的是(插件)工具去完成。

Gradle Plugin + Freemarker

在编译时进行辅助类的建立

  • xml的加载可以在编译时直接生成Map关系维护。
  • 冗长规则则可以通过枚举来进行维护(枚举通过xml生成), 调用方式则改为jump(Bridge.HOTEL_LIST)

总结

该篇文章更多的写的是我在接受到这个需求时的心路历程,每个人在写代码的时候都有自己的感受。而从设计Router中其实能发现很多道理

没有一个模型放之四海而皆准的

比如一开始的 if-else ,当业务量很小的时候 , 这可能是一个非常好的处理方式。而当业务膨胀,当前结构不适用时需及时重构。

框架脱离了业务那就什么都不是,只有契合业务的框架,才是一个好框架。

思考比编码更重要

当接受到一个需求后,更多的是需要去思考,去设计。提炼出核心关键点、流程,针对流程梳理出核心生命周期非核心生命周期很重要


推荐阅读
  • vue使用
    关键词: ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
author-avatar
假如有梦想
这个家伙很懒,什么也没留下!
RankList | 热门文章