化:【后缀】使成为,使变成〖-ize;-ify〗—— 用在名词或形容词后面,以构成动词。如:绿化,电气化;现代化。
一、工程化
2.1 几个概念
工程
泛指某项需要投入巨大人力和物力的工作。
工程是指以某组设想的目标为依据,应用有关的科学知识和技术手段,通过有组织的一群人将某个(或某些)现有实体(自然的或人造的)转化为具有预期使用价值的人造产品过程。
工程是科学和数学的某种应用,通过这一应用,使自然界的物质和能源的特性能够通过各种结构、机器、产品、系统和过程,是以 最短的时间 和 最少的人力、物力 做出 高效、可靠 且对人类有用的东西。
- 十八世纪,欧洲创造了“工程”一词,其本来含义是有关兵器制造、具有军事目的的各项劳作,后扩展到许多领域,如建筑屋宇、制造机器、架桥修路等。
- 工程的主要依据是数学、物理学、化学,以及由此产生的材料科学、固体力学、流体力学、热力学、输运过程和系统分析等。
软件工程
将系统化的、规范的、可度量的方法用于软件的开发、运行和维护的过程,即将工程化应用于软件开发中。
它借鉴传统工程的原则、方法创建软件,以达到提高质量,降低成本的目的。
软件工程是一门指导计算机软件开发和维护的工程学科。是一门交叉性学科(需要用到多种学科的方法支持和指导),如:
- 计算机科学、数学用于构造模型与算法
- 工程科学用于制定规范、设计范型、评估成本及确定权衡
- 管理科学用于计划、资源、质量、成本等管理
2.2 工程化
工程化就是指将软件工程的技术和方法论运用在开发、部署、维护中,通过规范、工程框架和工具链来实现高效开发(降低难度、有效协同),降低成本,质量可控。
工程化包括环境搭建、开发、测试、构建、部署(发布)等一系列流程:
- 需求、方案的设计与评审
- 环境搭建:项目创建(脚手架)、框架选型、基础工具(请求库、路由库、基础视图组件等)选型等
- 开发:版本管理(分支、commit、review等)规范、动态化方案、组件化方案等
- 规范先行,约束工作流。(不要说什么有时间再重构。开发上线后的代码再次修改、测试这是人力浪费)
- 测试:测试用例,自动化测试
- 构建、部署:CI/CD(自动化持续集成发布)
- 监控、故障解决
不同视角对工程化的需求和理解也是不一样的:
- 中小业务团队 — 效率
- 复杂业务团队 — 统一、治理
- 中台团队 — 抽象、赋能
- 团队中的个人 — 认知成本、效率
设计原则:
- 没有银弹 — 某些好的实践未必适合我们团队;
- 不盲目投入 — 认清当前所处的阶段和主要矛盾。
工程化不是指某个工具,工具化只是实现工程化的方式
二、跨平台
跨平台(英语:cross-platform software、multi-platform software、platform-independent software)泛指编程语言、软件或硬件设备可以在多种操作系统或不同硬件架构的电脑上运作。
三、动态化
3.1 什么是动态化?
移动动态化:移动指的是移动端,包括安卓、iOS。动态化则是动态部署和逻辑下发到客户端的能力。移动动态最好的状态就是让移动应用和 Web 一样,想发就发!
3.2 移动端动态化方案
从「支持动态化的程度」、「与原生体验的差异」、「方案集成与功能开发成本的高低」三个维度出发,将市面上的移动端动态化方案分成三个方向(为了节约成本,移动端的动态化方案一般也会兼顾跨平台):
- Web增强(Web+):方向主要基于 WebView 实现,能够进行快速的迭代。不过在性能和稳定性上略差,使用场景有限。
- GPL 和 DSL 可以看作
Native 增强(Native+)
方向的子方向,Native+ 方向基于 App 内自带的语言解析器,独立于 WebView 实现动态化逻辑的解析、布局和渲染。- 基于GPL的 Native增强(GPL):
- 这个方向是移动端动态化的热门方向。React Native、Flutter、NativeScript 等等,实现原理各不相同;
- 共同的特点是利用了通用编程语言器,可以是系统提供的(如 Javascript),或者是集成到 App 的(如 Dart),再辅以某个布局系统和渲染方案,由于语言是图灵完备的,可以提供完美的动态化解决方案;
- GPL 方案虽然在动态化能力、渲染性能方面效果出色,但 Framework 体积、学习和开发成本等方面却也是相当高。于是产生了基于领域专用语言的解决方案。
- 基于DSL的 Native增强(DSL) :
- 此类方案通过采用适合专业场景的的 DSL 解析云端下发的逻辑结构,舍弃一部分动态化灵活性,换取其他方面的优势。
- 基于GPL的 Native增强(GPL):
四、容器化
伴随着上面的动态化方案诞生,容器化这个词在移动端也频繁的出现。
我个人对容器的理解,还是出于汉语的含义(容纳物品的事物),对应到软件开发中,借鉴WebView、RN、Docker容器的用途,我理解的容器是:能容纳 GPL代码/DSL代码/其他自定义结构化的数据的运行环境,能完成代码的解释、数据的解析转换。(CasaTaloyum说是理解没什么问题,但如果有更权威的理解,请知会我一声…)
美团外卖客户端容器化架构的演进中对容器化的理解:将前端呈现业务的环境抽象出来,将能力进行标准化,形成统一的容器,通过容器去屏蔽平台和端的差异。容器提供上层标准统一的能力接口,使得业务开发人员专注于容器内的业务逻辑的实现,最大复用已有的能力,而不用关注现在的环境是Android还是iOS,现在的端是美团App还是大众点评App。
容器和远端达成呈现协议,使得端上的内容具备随时可变化的能力。容器化架构的实现是存在一定前提的,如果业务的发展本身处在一个探索阶段,还有较多可变的因素,是无法形成稳定的能力层的,这时候建设容器化架构反而使得架构偏向复杂。但对于外卖业务场景来说,经过多年的沉淀固定,外卖业务逐渐形成了一套稳定的业务形态,已经进入到场景细分和快速迭代业务模块的阶段。在这样的阶段下,容器化架构才有可实施的前提。
容器在不同场景下的含义:
- 字面意思,容器就是指容纳小物品的事物,比如:
- UIStackview
- 自定义的Container视图组件
- 前端组件化开发中的页面充当的角色(将一个页面分成很多个组件,此处页面就相当于是个纯粹的容器)
- 代码/数据的运行环境,完成代码的解释/数据的解析。比如(从小到大):
- 美团,后端返回协商好的结构化的数据,移动端对数据进行解析,还原成界面的layout。进行视图创建、渲染。
- 动态化是整个外卖业务的发展方向。提单页的动态化建立在容器化的基础之上,在完成容器化之后,就具备了动态化的基础。当前提单页的动态化,所指的主要是模块层级的动态化,提单页的各模块展示顺序、展示与否,都可以完全由根据服务端下发的数据决定,各模块可以自由地进行组合、拼装,实现提单页的动态配置。
- RN。在一定程度上,React Native和NodeJS有异曲同工之妙。它们都是通过扩展JavaScript Engine, 使它具备强大的本地资源和原生接口调用能力,然后结合JavaScript丰富的库和社区和及其稳定的跨平台能力,把javascript的魔力在浏览器之外的地方充分发挥出来。
JavaScriptCore
负责JS代码解释执行ReactJS
负责描述和管理VirtualDom
,指挥原生组件进行绘制和更新,同时很多计算逻辑也在js里面进行。ReactJS自身是不直接绘制UI的,UI绘制是非常耗时的操作,原生组件最擅长这事情。Bridges
用来翻译ReactJS的绘制指令给原生组件进行绘制,同时把原生组件接收到的用户事件反馈给ReactJS
。- 要在不同的平台实现不同的效果就可以通过定制
Bridges
来实现
- 要在不同的平台实现不同的效果就可以通过定制
- 服务器容器
- 美团,后端返回协商好的结构化的数据,移动端对数据进行解析,还原成界面的layout。进行视图创建、渲染。
- 上面的Docker容器(主要是为了一键部署)。个人:Docker容器的概念换到移动端架构中,有些不同:
- 如果都是容器(容纳物品的事物),iOS架构中的容器更偏向空的容器(不包含代码、数据)。而Docker容器,像是装满物品的容器(包含代码数据)。
- 容器化很多时候是为了方便实现动态化,而Docker容器的概念,就是个静态。
容器化是个很大的概念,在移动端中,常见其应用于:
- 跨平台
- 动态化
- 如WebView容器、Weex容器、RN容器、自定义DSL容器等
跨平台与动态化是功能实现。容器化是一种软件开发方法,其可以实现,但不仅仅只能实现跨平台与动态化等。
五、模块化和组件化、插件化
5.1 模块化
5.1.1 什么是模块
模块化编程是一种软件设计技术,它强调将程序的功能分成独立的、可互换的模块,这样每个模块都包含执行所需功能的一个方面所需的一切。—— Modular programming
模块:模块化的目的在于将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块之间通过接口调用。
- 将一个大的系统模块化之后,每个模块都可以被高度复用。
- 但是值得注意的是模块不等于功能,二者的关系大概为:功能 > 模块。一个功能可能包含多个模块。
5.1.2 横向拆分业务、功能模块
5.1.3 纵向拆分技术、架构模块
5.2 组件化
5.2.1 什么是组件?
基于组件的软件工程(CBSE),也称为基于组件的开发(CBD),是软件工程的一个分支,它强调对整个给定软件系统中可用的广泛功能的关注分离。它是一种 基于重用(reuse-based) 的方法,用来定义、实现松散耦合的独立组件,并将它们组合成系统。—— Component-based software engineering
定义与模块化很相似,都是主要为了对一个系统做拆分。
组件:通俗点就是组件化就是基于可重用的目的,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件。
- 组件的出现是为了解决全局工程中有很多重复代码的问题,是为了复用,而且划分力度是相对较小的模块。
- 组件化的另一个目的是为了解耦,把系统拆分成多个组件,分离组件边界和责任,便于独立升级和维护。
组件的特性:
- 通过接口访问:组件通过接口相互通信。 当一个组件向系统的其余部分提供服务时,它采用一个提供的接口,指定其他组件可以使用的服务,以及它们如何使用。这个接口可以看作是组件的签名(signature)——客户端不需要知道组件的内部工作(实现)来使用它。 这个原理导致组件被称为封装。
- 可替换的(substitutable):组件是可替换的,因此如果后续组件满足初始组件的需求(通过接口表示),则组件可以替换另一个组件(在设计时或运行时)。因此,可以用更新版本或替代版本替换组件,而不会破坏组件运行的系统。
- 可重用性(Reusability):可重用性是高质量软件组件的一个重要特征。 程序员应该以许多不同的程序可以重用它们的方式来设计和实现软件组件。
5.2.2 从界面入手,拆分可视化组件
5.2.3 从数据入手,拆分数据加工组件
大部分时候,拆分模块、组件都是以清晰的流程、逻辑为基础的,就如上图的过程,当流程清晰后,可以拆分复用的组件也就出来了。
5.3 插件化
插件:可以理解为是封装了一层对外调用的接口的组件。
插件的概念比较形象,一般存在一个“插拔”过程,所以要求可插拔的插件有一个相同的接口(这里所说的接口只是概念上的接口,即调用方法及参数等)。而组件是不存在这个相同接口的。
插件和组件的实质区别就在于:通过统一接口隔绝业务代码对于组件的直接依赖。
插件化常见的应用场景:
- Webpack、babel、vite、rollup等脚手架
- vscode、atom、chromium 等项目
- Vue:通常向Vue全局添加一些功能时,会采用插件的模式,它有两种编写方式:
- 对象类型:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行;
- 函数类型:一个function,这个函数会在安装插件时自动执行;
- Android
- iOS:BeeHive这个路由框架,是不是也相当于把每个组件当做一个插件了,每个组件实现生命周期方法,主项目会在合适的时机,调用各个组件的指定方法。
- BeeHive会给每个模块提供生命周期事件,用于与BeeHive宿主环境进行必要信息交互,感知模块生命周期的变化。事件分为三种类型:系统事件、通用事件、业务自定义事件。
Android中对组件化、插件化的定义:
- 组件化:
- 开发模式下面module本来就是一个独立app,只是发布模式下变成library。
- 换个说法:组件化开发就是将一个app分成多个模块,每个模块都是一个个组件,开发的过程中我们可以让这些组件相互依赖或者单独调试组件,但是最终发布的时候是将这些组件并成一个apk发布。
- 插件化:就是不存在发布模式开发模式,每个组件业务就是一个独立apk开发,然后通过主工程app动态加载部署业务组件apk。
5.4 总结(异同)
5.4.1 三者小结
模块:高内聚,松耦合,功能相对复杂,有多个统一接口。模块化开发的基础是框架。
组件:代码重用,功能相对单一或者独立,无统一接口。组件化开发的成果是基础库和公共组件。
插件:近乎组件,有统一接口,可以说是封装了一层对外调用的接口的组件。
5.4.2 组件和模块的异同
相同点:
- 组件化和模块化的中心思想都是分而治之。目的都是将一个庞大的系统拆分成多个组件或者说是模块。
区别:
- 模块化开发是横向分块,组件化开发是纵向分层。(说法都不绝对,一般来讲)
- 模块化强调的是拆分,无论是从业务角度还是从架构、技术角度,模块化首先意味着将代码、数据等内容按照其职责不同分离,使其变得更加容易维护、迭代,使开发人员可以分而治之。
- 组件化则着重于可重用性,不管是界面上反复使用的用户头像按钮,还是处理数据的流程中的某个部件,只要可以被反复使用,并且进行了高度封装,只能通过接口访问,就可以称其为“组件”。
- 进行模块化拆分时你可以完全不考虑代码重用,只是把同一业务的代码做内聚整合成不同的模块。只不过这样得到的成果相对简单,我们一般不会这样而已。(并不是说模块就不能被复用!)
- 一般来讲,模块的体量会大于组件。不过,大的组件也可以称为模块,小的模块也可以称为组件,所以,在我看来组件和模块的划分并没有那么的泾渭分明。随意两者的粒度的大小改变,两者是可以转换的。
这三个概念是经常同时出现在一个项目中的,我们往往对复杂大项目进行模块化划分的时候,也会进行组件化,而且插件化的本质是面向接口编程,对于组件化和模块化都是适用的,可实现随意插拔的灵活和高扩展性,属于项目架构的高端设计。
组件和模块的区别一文中的说法:
组件(Component)和模块(Module)又是一对容易混淆的名词,也常常被用来相互替换。两者是否有差异往往取决专业背景、所在领域、以及视角。
个人总结,从设计上来看,组件强调复用,模块强调职责(内聚、分离),或者说组件是达到可复用要求的模块。
Module, 中文为模块或模组。它的核心意义是分离职责,属于代码级模块化的产出。本身是一组具有一定内聚性代码的组合,职责明确。对外的接口可以是松散的,也可以是集中的。SEI的定义如下:An implementation unit of software that provides a coherent set of responsibilities。它以问题分解的形式,来解决软件设计问题。它更强调一个内聚的概念,形式上可以是Java中的包,也可以是一个源代码目录。
Component,中文称为组件,或者构件。使用非常比较广泛,它的核心意义在于复用,相对模块,对于依赖性有更高的要求。
我大概整理了一下,两个定义 (参考Component based software programming):
- 可以复用的模块,概念上与模块基本等同,只是明显有依赖性的要求。(最早提出时概念)。
- Component-Based Software Development中提倡的组件定义如下(Component Software):
- 除了完成某个特定功能外,还要具备如下条件的代码组合:
- 符合特定的接口要求(交互的要求)。
- 具有明确的上下文依赖 (复用的要求)。
- 它可以独立发布(二进制或源代码的形式),也可以进行组合。这样软件开发就变成了组件的组装了。
- 除了完成某个特定功能外,还要具备如下条件的代码组合:
5.5 前端与iOS中的组件化
在前端、iOS、Android中,都会有组件化的说法(Android先不讨论)。不过与上面的组件概念都稍微有所出入。
以复用为基础,定义、实现许多松耦合的独立组件(Component),再将组件组合成为系统。
前端中的组件化:
- 横向划分模块、纵向划分组件
- 前端中的组件
Component
可以简单理解为页面组成部件:页面组成部件(header、footer、nav、search等)。不是以重用为基础定义组件 - 前端中数据加工一般不称为组件,更倾向于
utils
。
iOS中的组件化:以蘑菇街的组件化文章图为例:
- 大到业务模块,小到视图、数据加工组件,都统称为了组件。可以简单理解为APP组成部件。
- 这个叫法在起初接触组件化概念的时候给我造成了很大的困惑。一度怀疑自己的理解狭隘了….,直到看到Bang的文章,找到了同感。
首先我觉得”组件”在这里不太合适,因为按我理解组件是指比较小的功能块,这些组件不需要多少组件间通信,没什么依赖,也就不需要做什么其他处理,面向对象就能搞定。而这里提到的是较大粒度的业务功能,我们习惯称为”模块”。为了方便表述,下面模块和组件代表同一个意思,都是指较大粒度的业务模块。 —— iOS 组件化方案探索 — Bang’s blog
六、参考链接
- 工程化
- 动态化
- 模块化、组件化
- IOS组件化方案