热门标签 | 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 ,当业务量很小的时候 , 这可能是一个非常好的处理方式。而当业务膨胀,当前结构不适用时需及时重构。

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

思考比编码更重要

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


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
author-avatar
假如有梦想
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有