1.1 什么是组件化?
组件化简单概括就是把一个功能完整的App或模块拆分成多个子模块, 每个子模块可以独立编译和运行, 也可以任意组合成另一个新的App或模块, 每个模块即不相互依赖但又可以相互交互, 遇到某些特殊情况甚至可以升级或者降级
1.2 为什么要组件化?
现在的项目随着需求的增加规模变得越来越大, 规模的增大带来了很多烦恼, 各种业务错中复杂的交织在一起, 每个业务模块之间, 代码没有约束, 带来了代码边界的模糊, 代码冲突时有发生, 更改一个小问题可能引起一些新的问题, 牵一发而动全身, 增加一个新需求, 瞻前顾后的熟悉了大量前辈们写的代码后才敢动手, 编译时间也不在断增加, 开发效率极度的下降, 在这种情况下组件化的出现就是为了解决以上的烦恼
1.3 分析现有的组件化方案
很多大厂的组件化方案是以多工程+多 Module的结构(微信, 美团等超级App更是以多工程+多 Module+多 P 工程(以页面为单元的代码隔离方式)的三级工程结构), 使用Git Submodule创建多个子仓库管理各个模块的代码, 并将各个模块的代码打包成AAR上传至私有Maven仓库使用远程版本号依赖的方式进行模块间代码的隔离
1.4 如何选择组件化方案?
按照康威定律, 系统架构的设计需要根据组织间的沟通结构, 因为现在大部分项目的规模和开发人员的数量以及结构还不足以需要某些大厂发布的组件化方案支撑(大厂的组织结构和项目规模都非常庞大, 他们的方案不一定完全适合所有公司的项目), 进行更严格更细粒度的代码间以及模块间的隔离, 盲目的使用某些组件化方案, 可能会带来开发效率降低, 开发成本远大于收益等情况, 性价比变低, 作为项目负责人, 应该根据项目目前的规模以及开发人员的组织结构去选择目前最适合的组件化方案, 做到以项目实际情况去制定技术方案, 而不是盲目跟随某些大厂的技术方案让项目和开发人员花费大量时间去调整和适应
2 组件化方案描述
Component_base 目前采用的是单工程+多 Module的结构, 由于Demo较小仅仅为了展示基本规范, 所以也只是采用源码依赖并没有做到远程版本号依赖组件, 代码管理也只是采用单仓库+多分支的方式, 这样也是对于开发初期, 项目规模还较小, 开发人员也较少时, 开发效率较高的方案, 如果您的项目规模较大, 开发人员众多, 就可以采用上面提到的多工程+多 Module, 并使用私有Maven仓库管理组件版本
世界上没有一个方案可以完美到兼顾所有情况, 并且还满足所有人, 所有项目的需求, 所以项目负责人必须按照项目实际情况做出灵活的调整, 才能做出最适合自家项目的方案
2.1 架构图一览
Component_base 组件化架构图
2.2 架构图详解
目前架构一共分为三层, 从低到高依次是基础层, 业务层和宿主层, 由于目前项目较小人员较少所以三层都集中在一个工程中, 但您可以根据项目的规模和开发人员的数量拆分成多个工程协同开发
2.2.1 宿主层
宿主层位于最上层, 主要作用是作为一个App壳, 将需要的模块组装成一个完整的App, 这一层可以管理整个App的生命周期(比如Application的初始化和各种组件以及三方库的初始化)
2.2.2 业务层
业务层位于中层, 里面主要是根据业务需求和应用场景拆分过后的业务模块, 每个模块之间互不依赖, 但又可以相互交互, 比如一个商城App由搜索,订单,购物车,支付等业务模块组成
Tips: 每个业务模块都可以拥有自己独有的 SDK 依赖和自己独有的 UI 资源 (如果是其他业务模块都可以通用的 SDK 依赖 和 UI 资源 就可以将它们抽离)
2.2.2.1 业务模块的拆分
写业务之前先不要急着动手敲码, 应该先根据初期的产品需求到后期的运营规划结合起来清晰的梳理一下业务在未来可能会发生的发展, 确定业务之间的边界, 以及可能会发生的变化, 最后再确定下来真正需要拆分出来的业务模块再进行拆分
2.2.3 基础层
基础层位于最底层, 里面又包括核心基础业务模块、公共服务模块、基础 SDK 模块,核心基础业务模块和公共服务模块主要为业务层的每个模块服务,基础 SDK 模块含有各种功能强大的团队自行封装的SDK以及第三方SDK, 为整个平台的基础设施建设提供动力
2.2.3.1 核心基础业务
核心基础业务为业务层的每个业务模块提供一些与业务有关的基础服务, 比如在项目中以用户角色分为 2 个端口, 用户可以扮演多个角色, 但是在线上只能同时操作一个端口的业务, 这时每个端口都必须提供一个角色切换的功能, 以供用户随时在多个角色中切换,
这时在项目中就需要提供一个用于用户自由切换角色的管理类作为核心基础业务被这 2 个端口所依赖(类似 拉勾, Boss 直聘等App可以在招聘者和应聘者之间切换)
核心基础业务的划分应该遵循是否为业务层大部分模块都需要的基础业务, 以及一些需要在各个业务模块之间交互的业务, 都可以划分为核心基础业务
2.2.3.2 公共服务
公共服务是一个名为CommonService的Module, 主要的作用是用于业务层各个模块之间的交互(自定义方法和类的调用), 包含自定义Service接口, 和可用于跨模块传递的自定义类
主要流程是:
提供服务的业务模块:
在公共服务(CommonService) 中声明Service接口 (含有需要被调用的自定义方法), 然后在自己的模块中实现这个Service接口, 再通过ARouter API暴露实现类
使用服务的业务模块:
通过ARouter的API拿到这个Service接口(多态持有, 实际持有实现类), 即可调用Service接口中声明的自定义方法, 这样就可以达到模块之间的交互
跨模块传递的自定义类:
在公共服务中定义需要跨模块传递的自定义类后 (Service中的自定义方法和EventBus中的事件实体类都可能需要用到自定义类), 就可以通过ARouter API, 在各个模块的页面之间跨模块传递这个自定义对象 (ARouter要求在URL中使用Json参数传递自定义对象必须实现SerializationService接口)
Tips: 建议在 CommonService 中给每个需要提供服务的业务模块都建立一个单独的包, 然后在这个包下放 Service 接口 和 需要跨模块传递的自定义类, 这样更好管理
掌握公共服务层的用法最好要了解 ARouter 的 API
点击查阅 ARouter 文档
简单使用:https://www.jianshu.com/p/fb20ab18c4cb?from=timeline&isappinstalled=0
2.2.3.3 基础 SDK
基础 SDK是一个名为CommonSDK的Module, 其中包含了大量功能强大的SDK, 提供给整个架构中的所有模块
软件复用的主要思想是组件化设计,实现松耦合的架构,业务设计同样追求这一理念,从业务流程的线性到以业务模快(组件)为中心,对业务活动进行分类聚合,达到业务组件化目的,这种方法就是CBM,接下来就从IBM的这篇介绍文章开始,做一个CBM方法、工具和案例的分享。
CBM:通向专业化的路径
市场环境日趋网络化。专业化经营不再是可有可无的选择,而是企业的必由之路。经济全球化不断冲破传统公司界面。企业的成功越来越依赖其绝对的竞争优势。在这种环境下企业得以生存的关键在于重点经营少数几个关键的业务。但是,如何才能使企业有效实现专业化呢?
流程优化的局限性
企业要想在今天得网络市场取得成功。流程优化是必要条件。而不是充分条件。进馆流程优化具有很大的吸引力,但是它仍然会让公司的流程变动十分复杂而僵化。在获得了一定的初期收益后,收益递减归类开始发挥作用。边际效益的增幅衰减,同时,成本减低的效率会越来越低。
更糟的是,因为流程是在内部进行优化的,这实际上增加了各个业务很大的集成成本。在大型的、复杂的组织中,这一问题尤其尖锐。问题的部分原因是:基于流程的优化会在不同的流程中将各个公司的同一种业务活动进行不同的优化。因此,随着流程的改进,会出现各种延伸到多个业务部门的互连,这将提高复杂性,并导致集成成本以二次函数的比例上升。因此,随着流程优化的成熟,它实际上会最终增加企业的复杂性。其结果就是:更高的成本、更低的灵活性和更慢的市场反应速度。
经验数据已经证明了这一点,即公司规模和股本收益率之间几乎没有相关性。一些研究甚至发现者两者存在负相关。换句话说,公司越大,获得实际的股东价值实际更低(见图3)。好听些的解释是,这暗示着规模效应其实并不像多数管理者以为的那样明显。不好听的解释是:从历史上看,某种程度上大型公司的传统业务模式破坏了大量的股东价值。不管怎样,流程优化远远不是包治百病的灵丹妙药。
1473149426333281.jpg
专业化不同于流程优化
为了提高电视时段、收音机时段和广告牌业务的销售效率,大型媒体和娱乐公司不断努力优化销售和市场流程。而广告客户们却不断改变游戏的规则。现在,客户对全套媒体服务组合需求日益增加。该服务组合通过协调多种媒体渠道、以单一价格向目标消费者提供服务。具有讽刺意味的是,流程优化的作用适得其反,客户无法预料的需求变得更难满足。
专业化能够扩大企业经营者的视野,避免发生这样的失误。媒体公司可从根本上建立“锁定、获取客户”的功能模块并为整个组织所共享,而不只盯着现行的生意流程做文章。这种专业化可提高公司的业务弹性和灵活性,从而更好的应对市场变化。它还可以推动业务单元获得优异的绩效并使各业务单元间实现成本协同——这是流程最佳化的两个主要目标。
对此管理者可引用组件化业务模型(CBM)概念,帮助企业实现内外部专业化。CBM可帮助管理者评估整个企业的目标和战略,同时有效利用内外部专业化的优势。该模型可以帮助公司不断扩张和发展而不增加复杂性,同时还能降低风险,推动业务绩效、提高生产率、控制成本、改善资本效率、增加财务的可预测性。
那么,什么是业务组件呢?
如图4所示,业务组件是构建专业化企业的功能模块。每个组件包含五个维度:
组件的业务用途(businesspurpose)是它在组织内部存在的目的,这表为该组件向其他组件所提供的价值。
为了实现业务用途:每个组件都要执行一系列相互独立的活动(activities)
组件需要各种资源(resources),如人员、知识和资产等来支持这些活动。
每个组件都根据自己的治理模式(governancemodel),以相对独立的实体方式进行管理。
像一个单独的企业一样,每个业务组件都可以提供和接收业务服务(businessservices)
例如:银行可以将自己的信用决策活动集中起来,形成一个独立组件,为获得规模效益,可以将过去分散在各个业务单元的相关员工、流程和资产集中在一起,还可以整合全公司的财务数据库,以提高决策活动所需信息的质量。集中保存信息帮助信息用评估人员在评估各个账户之间的各种信息后,做出更好的选择。基于对客户的信用风险情况更清楚的了解,银行可以更有效的交叉销售其金融产品。
要最大程度发挥组件化的优点,公司需要咨询汇总企业内部具有“高度凝聚力”的活动,即那些需要类似员工、流程和技术基础结构的活动。(详情参见“松散耦合和凝聚”部分)。在确定组件的业务边界时,银行应对这三方面都加以考虑,而不是仅仅考虑其中一两个方面。
以前银行会有五个不同的小组来处理信用评分,而经过简化后的新的信用管理组件则可以管理所有潜在顾客信贷活动,如管理申请流程、配置信贷资源以及信贷政策合规性管理等。
信用管理组件具备自身的管理结构和治理模式,从而具有高度的自主性。原则上,它能作为单独的业务向本公司提供服务。必要时,它还能为其他公司提供服务。
在运行过程中,新组建具有高度的协作性。它与公司内外部的其他组件协调工作。协助过程是通过组件间输入输出服务进行的。在需要输入服务以完成特定活动时,信用管理组件会从其他组件获得该服务输入(如客户信息和账户恢复等)。反过来,在信用评估、信用报告等其他业务组件需要时,信用管理组件就会向其他业务组件提供服务输出。预先定义的服务水平协议会规范所有这些交易的标准——如输入、输出格式、事件、数量、质量、支付和备份等。
通过这种服务导向的方式,信用管理组件既可以明确自身的业务边界,同时又可以通过松散耦合方式和其他业务组件进行协作。在业务环境发生变化时,每个组件都能够轻松结束就的链接,形成新的链接。
松散耦合和凝聚
业务组件的优势在很大程度上来源于其具备两个相关但截然不同的特性:首先,组件之间通过松散耦合方式进行链接,具备灵活、响应快、适用能力强的特点;其次,组件内活动的凝聚力强,可以对外提供效率高、质量好的服务。
组件间的交互具有松散耦合的特征。组件间的连接不是基于特殊的或定制化的“硬线”(hardwired)链接,而是基于明确定义的业务边界,并随时可根据服务请求而形成或断开连接。此外,松散耦合建立在通用的通信代码基础上,这样,既是不兼容的底层系统也可以根据需要随时进行连接。例如:网络银行可以通过启用售票机和Web门户使用呼叫中心功能,从而提高客户服务水平。组件松散耦合的特性可以使公司所提供和所使用的服务具备较强的可扩展性,同时,也可帮助公司更灵活的使用公司内外部的各种功能。在这种情况下,请求服务的组件只需考虑需要什么样的服务,而无需关心服务具体如何实施。的确,从外部看,组件就如一个黑匣子,没有必要关心其内部的运作情况。
从公司内部看,各种组件通过凝聚(cohesion)方式获得规模及效率优势,即将公司内部所有类似的活动汇总成一个模块。从这个意义上讲,构建组件有点像“按物归类”。要获得凝聚效应,则每个活动都必须唯一的属于一个组件,且组件之间的活动不应出现重复。
将这些活动集中的另一个好处是可以帮助管理者揭示企业内部的实际绩效差异。通过在集成的功能模块中推广专业操作,可提高客户服务的总体质量。事实上,这也便于在实际工作中推广“内部最佳操作方式”。
许多公司都在努力获得凝聚效应。当因特网首次作为新渠道出现时,许多公司就将其作为全新的业务方式对待,纷纷构建自己的网站,并为此推出单独的服务、交叉销售和市场营销活动。这种做法的结果是:公司业务混乱并让客户感到困惑。在访问web站点时,能看到一系列产品和市场信息,可当走进真是的商店或打电话给呼叫中心时却是另一种场景。这些公司诶有意识到:不管采用何种渠道,服务、销售和市场份额本身是具有高度凝聚性的活动。
最灵活、最高明的利用凝聚的方法是:一次性地构建功能,然后,在各渠道中重复使用,只需针对媒介特点适当调整用户界面,而困惑获得的服务、可供选择的产品、服务范围及公开的市场信息等均保持一致。如果忽视员工、流程和技术等方面活动的凝聚效应,公司业务的复杂性必然会大幅度增加。
CBM模型
正如上文所述:组件可将各种业务活动汇总起来,形成多个独立的模块,并在公司之间共享。但是,组件是如何在总体的业务模型内进行协作的呢:如图5所示,CBM模型可按照业务能力和责任级别两个维度对组件进行组织。通过这一模型,管理人员就可以设想当时的业务活动时如何通过一系列相互联系的模块运行实施的
按照业务能力划分各种活动并形成组件,并与人们从较高层面掌握该组件为企业提供的价值。不同行业中的不同公司在建立能力模型上会有不同的做法,但无论如何,各项活动都应该根据特定的能力进行排队。
为每个活动指定一个责任级别——即引导、控制和执行。还可以帮助管理人员充实组件的用途。组件的级别应该十分直观,尽管会存在例外情况。
引导。本级别的组件应该向其他组件提供战略方向和公司策略。此外,还应该促进组件间的配合。
控制。这些中层组件在引导级别和执行级别的组件之间发挥相互制衡的作用。他们监控业绩、管理例外情况并发挥看管资产和信息的作用。
执行。这些“现场的”组件所提供的业务行动可以促进企业的价值实现。他们处理各种资产和信息,供其他组件或最终客户使用。
三个责任级别有不同的优先考虑目标。如在执行级别,所强调的是保证员工的工作负荷和生产效率。这一级别的组件结构往往是可以轻松提供信息访问为目的而设计的。从技术的角度看,设计输入速度和实时可用性是关键。例如在客户使用ATM时,他们希望用户界面简单,且以十分简捷的格式提供下列准确的信息:即我的账户上有多少钱?
与执行层的活动相比,引导层处理的是较高级别的活动,如发布新产品。这一级别的员工为数不多,但对股东的价值影响重大。所以,其设计要点正好与执行层相反。发布新产品需要多个要素之间进行协作,包括市场、风险、财务、法规和信用等。需要所有这些要素都提供输入才能保证新产品发布成功。因此,工作流是关键性需求。从技术角度看,这些活动通常需要员工分析数据仓库中存储的大量、多维数据从而辨别各种模式和其趋势。因此,引导层的系统设计不应以数据输入速度为重,而是以分析的便利性、数据分析的广度和深度为目标。因为经常会调用数月之久的老数据,而且通常以分批的方式处理数据,所以并不需要实时界面。
CBM战略路线图
CBM不仅是一种设想组织未来的方法,还可以用来将理论转化为实践,从而推动企业内部和外部专业化发展。这个过程包括三个方面:第一:通过分析业务和市场环境,得出现有公司的组件视图,第二,在不断变化的行业环境中,根据重新规划方案向专业化方向发展,第三,是组织、营运基础设施向组件化企业方向不断优化。
形成企业的组件视图
通过使用CBM模型这一分析工具,公司可以得出企业的组件视图,从而确定出成为一家组件化企业所必须面对的差距和冗余。首先可以将现有的业务映射为众多组件。如前所述,这一初步分析包括如下活动:确定出各种具有凝聚性的活动,将这些活动组合成独立的单元并对其逻辑进行测试,最终形成组件映射。图6所示的是零售业的组件映射范例。尽管同一行业中各公司可能会对组件结构会有不同看法,但他们在本质上多数趋同。
组件映射可以帮助管理者从战略、操作层面分析现有业务。通过分析不同映射领域的相对业务价值,管理者就可以确定需要及时关注那些组件。如图7所示,这类分析得出的“热映射”突出了具有最大经济价值的组件。
1473150120841400.jpg
要确定出热映射的优先级,管理人员一般情况下会考虑下列问题:那些组件使公司具有最显著的市场特征?哪些组件对公司持续获得、增加利润的影响最大?哪些组件存在重大的成本、资本优化的可能性?
如:短期内可增加公司战略特性的领域就很可能被指定为热领域。对十分类似的业务,如共享服务中心,也可能会列入尽早考虑的名单中。一般情况下,将分散、重复的功能予以整合可以迅速获得收益。第一轮组件化变革中所获得的效率提高可以当做后续变革的支持力量。
洞察阶段之后是CBM架构阶段(见图8)。在这一阶段,公司对现有进行热映射处理,目标是确定出组件业务的未来远景与当前状况之间的差距——这揭示出该公司目前是如何组织起员工、流程和技术的。要掌握该公司当前功能和市场位置的所有情况,这些当前的数据必须牢固的扎根于经验数据,如组织图、成本推动因素、应用程序系列产品、技术投资、关键的绩效评测标准和现有的流程。
最后,在投资阶段,公司应该决定如何弥补各种差距:即:公司可以实现多达的飞跃?可以承担多大的变化?公司应该首先将侧重点放在那些领域上?哪些方面可以迅速取得成功?(符合这些标准的内容包括流程和组织单元之间重复出现的各种活动,尤其是那些将受益于规模增加、全球外包选择和共享信息的各种活动)。经过CBM分析的三个阶段,企业将得出一个转型路线图,它知道企业从某一个业务领域(如客户服务)尝试组件化。在这一领域中取得的初步成功,将为企业进一步开发组件提供经验和佐证。
前言 组件化方案目前有URL、Runtime、Protocol有三种,组件化方案采用基于Runtime的CTMediator
一、组件化的划分依据
1、组件化的划分跟粒度无关,只满足下列条件中的一种,即使它只有一个对象且不到50行代码,那就会把这部分组件化出来。
(1)这一部分是否能够自治,且相对独立。
(2)这一部分是否会被多个调用者调度。
2、组件化更多的是针对横向依赖下沉。业务相对于服务之间的依赖属于纵向依赖,把服务作为普通pod引入即可。业务和业务之间是横向依赖,必须组件化。
二、组件化注意事项
1、原先是delegate委托传值,组件化后调用需通过block回调来当中间人
2、target-action和category,相当于一对乔接口需要同一个人维护,比较不容易乱,一般一个组件就只有一个target
3、target-action是依赖于组件方,和组件方放同一个pod,命名域在被调用发,category封装成独立的pod,依赖中间人CTMediator,供调用方使用,命名域在调用方
4、cocoapods只是把图片复制过去,Xcode的asset机制就失效,外部要调用的话,只能使用图片的真实名字(即存放时的名字) [UIImage imageNamed:”图片存放名字”]
5、某个组件需要监听或者调用者需要监听某个通知的话,通知名放在组件中,外部要调用的话,通过category返回该通知名,方便回溯追踪
6、组件化方案是服务于基于OpenUrl的跨App调用
7、出于安全考虑,通过action是否有带native来区分action是否仅被本地组件调用,不能被跨app调用
8、针对于都继承同一个基类的情况下,如果能去掉基类,就去掉基类,如果实现不行的话,将基类独立出一个pod,各组件podspec依赖写该pod,有用到该组件的话,podfile也要写入基类pod
三、组件化的好处
1、各个组件都封装成独立的pod,pod引入到主工程的话,是以静态库形式提供的,所以会提高编译速度
2、以后业务变多和代码量变大的时候,其易于维护和排除问题的优点就会越发凸显
3、用已经封装好的组件pod,更易于快速出新产品
1、对组件化的理解
①什么是组件化?
组件化就是将一个项目拆分成若干个组件,分而治之。比如一个汽车的生产,也是将轮子、灯、座椅等等作为单独的组件,由各自的工厂去生产维护,生产轮子的就专门做轮子,生产座椅的就专门生产座椅,等各个组件都做好后再拿到组装厂统一调度组装使用。
在实际的开发中也是一样,比如我们经常用到的微信,有朋友圈、漂流瓶、聊天模块、支付模块等等众多功能,微信开发者也是按照组件来划分各自的开发任务的,比如A团队负责漂流瓶、B团队负责朋友圈等。然后在总项目中分别调用组件来使用。
当然,组件并不一定是这么大的业务模块,也可能是一个小UI,比如bander、按钮等等,这样在项目中多处地方用到的话就可以直接调用组件使用了。
组件化开发的好处有以下几点:
1、高复用性:组件创建后就可以被需要的地方调用,比如Bander,因为项目中可能会有多个地方用到bander,所以将bander抽成一个组件后,需要用的地方直接就可以拿来用,而不用在重写一个,提高了代码的复用性;
2、低耦合性:低耦合就是指各部分依赖程度低,比较独立。因为组件化开发是各自维护自己的组件,所以相对来说比较独立;
所以,组件化开发很适合多人开发的项目,组件间单独维护单独测试,简单方便。
②组件化、模块化、插件化
这三个概念很相近,我们一一来看:
首先,模块化,模块化是指将一个项目按照业务逻辑划分成若干个模块,比如购物类app可以划分为商品展示模块、购物车模块、订单模块、客服模块等等,将一个项目分成几层,这样可以保证每个模块的职能单一;
模块化虽然进行了分层开发,但是有个问题就是代码复用性不高,比如A团队开发商品模块时写了一个bander,而在B团队开发的订单模块中也会用到,但是B却没办法用,这个时候就出现了组件化。
组件化是在模块化的基础上的进一步演变,它划分的更细了,每个组件都是独立的,可以按照需要选择需要的组件组合起来就成为了整个项目。
而插件化,本质上也是模块化的一种,它也是按照业务逻辑进行划分,但不是划分成一个个模块,而是划分成插件(这些插件可以独立编译打包成为一个独立子app),
而上线的时候是将各个插件组合到一起形成一个大的app。同时因为插件化的加载是动态的,所以可以实现热修复。
//热修复原理
首先,生成新版本的apk与旧版本的apk的差异补丁包文件;
其次,使用热修复框架的Api在Application中去尝试加载指定路径的补丁差异包;
最后,只需要将补丁差异包宝贝到对应路径,代开有bug的App,在Applcation创建的时候就会将补丁包文件加载到内存中并且替换对应的方法。
虽然组件化和插件化拆分的部分都可以单独编译运行,但是两种还是有较大差异的:
复制代码
//划分单位
组件化的单位是组件(module)。
插件化的单位是apk(一个完整的应用)。
//实现内容
组件化实现的是解耦与加快编译, 隔离不需要关注的部分。
插件化实现的也是解耦与加快编译,同时实现热插拔也就是热更新。
//灵活性
组件化的灵活性在于按加载时机切换,分离出独立的业务组件,比如微信的朋友圈
插件化的灵活性在于是加载apk, 完全可以动态加载,动态更新,比组件化更灵活。
组件化能做的只是, 朋友圈已经有了,我想单独调试,维护,和别人不耦合。但是和整个项目还是有关联的。
插件化可以说朋友圈就是一个app, 我需要整合了,把它整合进微信这个大的app里面
其实从框架名称就可以看出: 组 和 插。
组本来就是一个系统,你把微信分为朋友圈,聊天, 通讯录按意义上划为独立模块,但并不是真正意义上的独立模块。
插本来就是不同的apk, 你把微信的朋友圈,聊天,通讯录单独做一个完全独立的app, 需要微信的时候插在一起,就是一个大型的app了。
复制代码
插件化更关注动态加载、热更新、热修复等‘插拔’技术,目前热门的插件化方案有:阿里的atlas,360公司的RePlugin,滴滴的VirtualAPK等等;
所以,组件化、模块化、插件化都是将一个项目划分成若干个部分,分而治之,只不过各自划分的依据和划分的单位不同。
2、组件的拆分
我们在了解清楚组件化的意义后,那么接下来进行组件化操作,第一个问题就是组件怎么拆分?
组件划分不细致会造成很多冗余代码,或者划分的太细致则会加倍增加工作量。换句话说,组件的划分决定了整个工程的质量。
我会从以下几个方面来划分组件:
基础通用组件
基础业务组件
UI公共组件
独立业务组件
基础通用组件的划分
我们这么来理解基础通用组件,变化不大,而且基本上每个项目都用到,项目中都要围绕这些组件来实现业务功能的组件。例如我们在Pods中的AFNetworking、SDWebImage、FMDB,以及常用到的Category等。这些组件或许需要根据业务进行一些二次封装,但是每个项目中对它们的改动都不大。
使用第三方库应该尽量进行二次封装,封装更适用于业务的组件,或者封装成一个接口类,避免在换第三方库的时候整个工程逐句代码修改。下面有几篇关于基础组件封装的文章供大家参考:
网络层 HKHttpManager
URL跳转路由 ALRouter
Category的设计AOP代替继承
基础业务组件
我们可以将类似用户行为统计、异常上报、推送服务、消息通道、支付、通用宏定义头文件这种根据业务为基础的服务SDK作为基础业务组件,最好每个基础业务组件都各分其责,避免某些组件没用到某些功能而造成代码冗余。
UI公共组件
UI也有公共部分,建议在进行开发之前可以和设计师取一下经,或许他们已经做好了公共组件~这样划分根据他们的来就好了。
UI组件种类繁多,大家记得根据 公共的原则来抽离就行..
独立业务组件
根据业务的独立性来划分,例如我将我司的电商APP可以划分为首页组件、购物车组件、登录注册组件、订单组件、用户中心组件、商品组件。
独立组件一定要保证独立性,避免首页含有商品组件等这种情况,每个组件都通过Route来交互,必要时提供对应的接口。
总结
以上内容总结为下图,只要划分清晰了才能提高代码效率,组件化才有意义。
参考资料
3、创建本地仓库与远程仓库
讲完概念和划分后,我们接下来看一下怎么去做一个组件呢?比如我们现在想创建一个工具类CDUtils组件:
①本地仓库的创建
1.完成功能开发工作,实现组件功能,也就是把代码写好,实现对应的功能
注意,组件化的顺序应该是:先实现好组件功能在制作组件化。所以应该先开发好组件的功能(就跟正常开发项目一样,可以添加依赖库实现想要的功能),完成功能后开始制作组件。而不是先组件化然后在实现功能,这个就颠倒了。
2.然后打开终端 切换到改项目路径下 输入
pod lib create XXX (XXX:代表想要封装的组件名称, 这个根据自己的需求而定)
3.然后会出来一些对组件工程的设置:
复制代码
What language do you want to use?? [ Swift / ObjC ]
ObjC(开发语言设置,根据自己而定,这里为ObjC)
Would you like to include a demo application with your library? [ Yes / No ]
Yes(是否需要创建一个demo用来测试你的组件,这里选择Yes,是为了之后对写好的组件进行测试)
Which testing frameworks will you use? [ Specta / Kiwi / None ]
None(测试框架)
Would you like to do view based testing? [ Yes / No ]
No(是否要做基础的视图测试)
What is your class prefix?
XX (文件前缀)
复制代码
4.当创建完成之后,工程会自动打开,这时我们发现项目的结构发生了变化:
在pods工程Development Pods文件夹下有一个replaceMe.m文件,我们要将我们写的东西(colorTool.h和colorTool.m替换到这里),注意这里是要文件的真实替换而不是在目录中的顺序变化,替换完成是下面这个样子:(添加功能的代码一定放在Classes)
这个时候,我们就已经创建好了一个组件colorTool存放在本地库,我们就可以在本地使用了,比如说在刚才那个项目中,添加pod管理
pod init
然后就会出现一个podfile,我们在里面添加组件及地址
复制代码
platform :ios, ‘9.0’
target ‘cdutils’ do
pod ‘ColorTool’, :path =>’ColorTool’
end
复制代码
然后执行pod install 我们就可以使用了
当然,在本地其他项目也可以使用,
比如我们新建一个项目,然后pod init 只不过在podfile文件中path需要写全地址 然后pod install 发现也可以使用
platform :ios, ‘9.0’
target ‘weew’ do
pod ‘ColorTool’, :path =>’/Users/uerName/Desktop/cdutils/ColorTool’
end
这里的全地址就是podspec所在的路径
但我们在实际开发中代码不能只存放在本地,需要存储在远程,让团队都可以用,所以我们还需要将本地仓库的组件推送到远程仓库。
②远程仓库
既然是远程仓库,那我们需要选择一个远程代码托管平台,常用的有码云和github两种,因为码云访问速度快和私有库免费,所以我们这里选择了码云,两者在使用上都是相同的,无非就是远程地址不同而已。
5.在码云上创建项目,获取项目地址
6.配置spec文件,这个文件很重要,它描述该库某一个版本的信息,比如库的名字、版本号、描述、依赖库等等,每一个组件都有自己的spec文件。
所以,我们需要修改spec文件,比如说修改里面的source内容等信息,举个例子
复制代码
Pod::Spec.new do |spec|
//库名
spec.name = ‘ColorTool’
//版本号
spec.version = ‘0.1.0’
// 授权协议
spec.license = { :type => ‘MIT’, :file => ‘LICENSE’ }
//库的首页
spec.homepage = ‘‘https://gitee.com/github-xxxxx’
//作者
spec.authors = { ‘xxx’ => ‘xxx@126.com’ }
//库的概要
spec.summary = ‘A short description of ColorTool.’
// 库的源路径和版本号 这个是最重要的 一定要写自己的组件远程地址
spec.source = { :git => ‘https://gitee.com/github-xxxxx/colorTool.git’, :tag => ‘v3.1.0’ }
//源文件,这个库仅包含Reachability.h和Reachability.m文件
spec.source_files = ‘ColorTool/Classes/*/’
//使用到的系统框架
spec.framework = ‘SystemConfiguration’
// 是否支持ARC
spec.requires_arc = true
end
复制代码
当对内容修改完成之后,保存。
7.拿到地址后,切换到组件根目录(也就是.podspec文件目录)下 将代码提交到远程仓库:
注意,在提交代码是一定要保证spec中的source是远程地址 否则pod install的时候source不对导致不能正确执行
复制代码
cd /Users/userName/Desktop/cdutils/ColorTool
//记得后面一定要有 .
git add .
git commit -m “初始化”
//添加远程仓库(根据自己的项目地址来操作)
git remote add origin https://gitee.com/xxx/HFMyTest.git
//推送到远程
git push -u origin master -f
//打tag 注意:这里的tag号必须和.podSpec文件的版本号一致
git tag 0.1.0
//将tag推送到远程
git push –tags
复制代码
这样我们就将组件功能代码添加到了远程,接下来我们在将spec文件推送到远程。
8.如果还没有创建spec远程仓库,可以创建一个,也是在码云创建,创建过程和上面写的一样,只不过这个不是存放具体代码,而是存放各个组件的spec文件。
9.如果没有将远程spec仓库添加到本地,可以通过下面指令添加到本地:
pod repo add 自定义一个Specs名称 公司Specs地址
在这个地方可以看到我们刚才创建的本地spec仓库
10.将spec推送到远程,别人要想pod ‘colorTool’时是找不到spec文件的,也就没有办法根据source去拉代码,所以需要将spec推送到远程。
pod repo push MySpecs(同9) 组件名字.podspec
pod repo push MySpecs ColorTool.podspec –use-libraries –allow-warnings
如果有警告,要忽略的话
pod repo push MySpecs 组件名字.podspec –use-libraries –allow-warnings
包含私有库 (这个暂时还没有用过)
pod repo push MySpecs 组件名字.podspec –sources=oschina-qx2
11.这样,我们就将spec推送到了远程,可以使用pod search ColorTool来查询是否已经提交到cocoapods;
12.每当我们要迭代版本的时候,除了修改业务功能代码变动,就是要修改.podspec这个文件,只用修改版本号,重复6、7、10即可。注意的一点是我们是把组件提交到了码云上,所以从码云上clone代码修改迭代的话可能不太好弄,因为只有组件缺少环境,所以我们可以吧我们写组件的这个xcworkspace文件也存到远程,这样就可以在这里面方便的修改组件了。
如果我们迭代组件版本更新到远程后,发现组件还是旧版本,可以做如下操作
//1.更新本地仓库
pod repo update MySpecs(本地仓库名)
//2.删除项目中的删除podfile.lock+xcworkspace+Pods文件 重新执行pod install
pod install
这样,当其他人在远程想用我们的组件的时候,就可以了:
①先将我们的远程spec仓库添加到本地:pod repo add 自定义一个Specs名称 公司Specs地址(这个是需要验证账户密码的)
②添加pod管理,在podfile文件中添加组件:
pod ‘ColorTool’, :git => ‘https://gitee.com/github-13584768/colorTool.git’
其实这里不用指定git的具体地址,但是不指定的话总是显示找不到colortoo的说明文件,重置spec库也没用,这里就指定了 更多关于podfile文件用法
添加完组件我们就可以使用了。
本地Specs仓库位置:在终端输入:pod repo,即可显示出当前所有的仓库地址及名称,找到对应的Specs,复制路径并前往文件夹。其中存放着我们组件的版本号文件和文件下的.podspec文件.
组件中有pod其他框架的情况:
有的时候我们的组件会用到其他第三方框架或者我们自己写的其他组件,比如我们现在有个弹框工具组件,需要依赖SDWebimage,所以在组件开发的时候我们在podfile中通过pod ‘SDWebimage’ 引入这个框架进行开发调用。但当别人用我们的组件就会出现的时候,不知道组件依赖SDWebimage,所以会出现找不到SDWebimage的错误,那么这个时候我们应该在组件中的podSpea文件中s.dependency说明一下我们这个组件依赖了哪些框架,这样系统会自动配置好我们组件依赖的框架环境,保证我们的组件正常使用。(这个属性默认是注释的,我们需要去掉#并填入我们自己需要依赖的框架)
写好之后保存然后进行步骤12中的操作。如果依赖多个库,可以并列写:
s.dependency 'AFNetworking'
s.dependency 'SDWebImage' 组件中有图片等资源的情况:
比如上面这个组件,我们在使用的时候发现图片加载不出来,这是因为我们少做了三步:
1.是否将图片放到了Assets文件夹中(这里面放的是文件,比如.png等文件)
2.是否配置podspec文件的资源属性:
//这个属性默认是注释掉的 意思就是’alertToolLib/Assets下的所有文件都放到alertToolLib.bundle中 这个alertToolLib/Assets/路径是根据自己实际情况需要的 看自己图片路径是什么样的
s.resource_bundles = {
‘alertToolLib’ => [‘alertToolLib/Assets/’]
}
3.加载图片资源的路径是否正确:
通常如果我们在主工程调用主工程的资源时,可以直接imageName或者[mainbundle pathForResource]读取,但是在用pod进行管理的时候,pod中的资源文件也会变成bundle加入到mainBundle中,但是由于资源文件的bundle并不是mainBundle,所以这种方法是行不通的,关键是要取到资源相关联的bundle
其关系是这样的
所以我们要到对应的bundle中去加载图片
更多资料
组件中有加载xib的情况,这个原因和加载图片一样,也是因为路径问题,解决方案和图片一样(xib和图片一样都属于图片资源,所以都要存放到Assets文件夹中)
参考资料
4、组件间的通讯
当我们写好若干个组件之后,就出现了一个问题,比如商品详情页的组件想跳转到购物车组件,那不同的组件间是如何通信的呢?
在没有组件化的时候,我们的做法是在详情页中引入购物车的头文件进行调用,但是这样的话会是代码耦合性非常高,各个部分相互引用,当我们需要把某个东西拿出来用的时候,发现要删减很多东西,结构如下所示:(箭头表示引用)
上面这个结构太错综复杂了,对一个模块的修改往往影响多的地方,后期维护成本大,所以我们需要用其他方式来访问其他模块,让模块间相互独立。比如现在常用的两种方案,通过路由或url来访问其他组件(模块),我们一一来看。
①路由CTMediator(runtime+addtargetAction)
鉴于上面模块间的关系太复杂,我们需要想一个办法就是不希望导入其他模块的头文件但仍然可以使用该模块,我们想到了一个方法就是建立一个中间件,这个中间件导入了我们所有要用到的模块的头文件。我们想用其他模块的其他功能直接调用这个中间件的一个方法就行。
我们直接引入中间件 调用器方法就行:
复制代码
#import “middleware.h”
@interface ViewController ()
@end
@implementation ViewController- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{
[middleware alertToolShowLoadingProgressIndication];
}
复制代码
上面的模块间关系就变成了↓↓
这个时候我们就可以不引用其他模块的头文件来调用其他模块功能了,这样可以有效解决高耦合的问题的问题了,但是这样做还有一个问题,
那就是模块与模块间虽然不耦合了,但是模块间与中间件相互引用耦合了,而且中间件功能太多太复杂了,不好维护,还可以继续优化。
这个时候我们想到了runtime中有两个方法:
//根据类名字符串获取同名的类
Class cla = NSClassFromString(@”AlertTool”);
//根据方法名字符串获取同名方法
SEL selector = NSSelectorFromString(@”showLoadingProgressIndication”);
我们在获取到类和方法后可以根据performSelector方法来实现调用
Class cla = NSClassFromString(@"AlertTool");
SEL selector = NSSelectorFromString(@"showLoadingProgressIndication");
[cla performSelector:selector withObject:nil]; //以上三行的实际效果就相当于[AlertTool showLoadingProgressIndication]; 所以我们可以将中间件与runtime结合,根据指定类名和方法名让中间件实现相应功能:
这样模块间的关系就变成了这样了:
这样的话,只让模块对中间件依赖,中间件完全不依赖任何模块,我们所说的解耦合其实也就是这种效果。每个模块的负责人都不用再担心另一个模块如何,只需要和中间层进行沟通即可。(CTMediator就是这个中间件)
这个方案很好的解决了耦合问题,但是还存在一个问题,那就是我们是直接告诉中间件哪个类名哪个方法名的,但是在实际多人开发中,我们是不知道其他人写的组件类名和方法名是什么的?
所以这就需要组件的开发者提前将方法名暴露出来,也就是每个组件创建一个target类(由组件开发者维护),其内部定义了组件对外暴露的action(方法)。和组件通信时,其实质是调用一个特定的target-action的方法。target类的类名必须以Target_开头,比如Target_A,action的方法名必须以Action_开头,比如Action_nativeFetchDetailViewController。注意,暴露出来的这个target类并不是这个组件的具体实现,它只是为了方便调用者使用,target类的实现文件中会引入组件的头文件,实现声明文件中的功能,从而达到调用组件的目的。
@interface Target_A : NSObject
@end
复制代码
//Target_A.m
#import “Target_A.h”
#import “DemoModuleADetailViewController.h”
@implementation Target_A
//这里需要注意的一点是 因为我们是通过runtime来调方法的 参数也是通过params传递进来的字典 所以在Action_方法中的参数就是字典,字典中可以包含我们需要的值
@end
复制代码
另外,我们通过查看CTMediator的源码发现还有以下两个注意点:
// 源码中拼接方法名的时候都加上了:所以这就导致我们在实现Action_方法的时候都要写上参数dic,当然写上的话我们在调用的时候传nil就行 反正也不会用到
NSString *actionString = [NSString stringWithFormat:@”Action_%@:”, actionName];
//NSSelectorFromString是动态加载实例方法 所以这就要求Action_方法都得是实例方法,不能是类方法
SEL action = NSSelectorFromString(actionString);
这样的话每个部分都是单独的了,中间件涉及不到其他引用,可以拿出来放到其他项目中用,组件也涉及不到其他引用,可以拿出来放到其他项目中用,开发者只需要根据暴露出来的target-action去中间件中调用就行。
UIViewController *viewController = [[CTMediator sharedInstance] performTarget:@”A” action:@”nativeFetchDetailViewController” params:@{@”key”:@”value”} shouldCacheTarget:NO];
这样,我们就可以通过中间件进行调用了,注意的是通过中间件调用不需要写暴露出来的类名和方法名的前缀,也就是Target_和Action_。
iOS组件化通用工具浅析
iOS 从零到一搭建组件化项目框架
②其他方案:
URL(蘑菇街),这种方式没有用过,想要了解的可以看一下下面几篇文章
CTMediator作者的架构方案
组件化在蘑菇街
蘑菇街 App 的组件化之路
5、组件的使用
在组件化的实际开发中,我们可以通过上面的流程去开发,比如我们仍然拿alertTool这个组件来说,
1.这个附件依赖了CTMediator和SDWebimage两个库,所以我们要在spec文件中进行配置;
2.组件代码中要用到一些图片资源,我们放到Accests文件中,组件中使用这些资源的时候出了在spec文件打开资源属性外,还要注意调用的位置是在自己组件内的bundle中;
3.组件的实现代码,我们也可以分为123三个部分,1是代码的具体功能实现,2是将代码的类名和方法名都暴露出来给调用者使用,这里面需要注意的是Action_方法都是实例方法且都得有参数(参数我们一般都选择NSDictionary),3就是写一个CTMediator的分类,这个分类中是对CTMediator调用过程的一个封装,这样可以更加方便调用者使用。2和3都是需要组件开发者来创建维护的。
调用者的使用:简单方便
复制代码
#import “ViewController.h”
#import <alertToolLib/CTMediator+alertTool.h>
@interface ViewController ()
@end
@implementation ViewController
架构衍生理论和工具之——组件化业务模型(component business model, CBM)
2019-04-26 18:00
市场行业环境瞬息万变,新的业务模式不断诞生,企业随时面对跨行业的竞争和挑战。当前的企业,无论规模多大,都不可能完全控制端到端的行业价值链,这就要求企业必须专注于自己拥有绝对优势的领域,也就是说,企业必须通过专业化整合实现专业化经营。
一、企业的专业化整合
专业化整合可以让企业更加专注于自己擅长的领域,例如苹果公司的IPhone,所有的生产制造环节都采用外包的形式由专业的制造企业负责,苹果公司则专注于产品设计、创新和营造生态环境。专业化整合没有影响苹果公司的核心能力塑造,反而使其占据价值链的顶端,赚取了价值链的绝大部分利润。
专业化整合可以大幅提高企业的规模优势,增加产品和服务的利润。同时,企业也可以利用协作企业的市场和销售渠道,增加收入并获得更多的增长机会。
专业化的企业需要更多的管理投入。
首先企业的外部合作伙伴越多,签约的成本和协作成本就会大幅上升,企业的战略响应速度也会下降。外部专业化还需要企业增加协调、交互和通信的成本,企业需要增加协调的网络和渠道。所以外部专业化的企业需要增加架构、流程和信息化领域的投入。
此外,企业在进行外部专业化的同时,也在进行内部专业化活动。内部的专业化能够消除非增值业务活动;整合重复的业务活动以降低损耗;集中处理业务活动以实现规模经济;将业务活动重新安置到成本更低的地区;协调各种业务活动以缩短生产周期等。内部专业化的企业需要通过流程优化提高企业运作效率,促进企业内部协作,让更多的员工参与到跨组织的团队中,同时企业各部门共同分担技术成本和风险,提高企业的质量和效率。
总之,专业化的企业的终极目标就是通过流程优化实现企业业务的模块化,形成一系列单独的、模块化的业务模块运行,通过业务模块与企业内外部的业务进行交互,为企业的战略提供支持。
那么,企业如何对业务进行建模,构建整体的业务沙盘呢?组件化业务模型(component business model, CBM)就是帮助企业实现内外部专业的有效工具。
二、组件化业务模型(CBM)
组件业务模型(CBM)是IBM创造的业务模型组件化的方法,通过将组织活动重新分组到数量可管理的离散化、模块化和可重用的业务组件中,确定改进和创新机会,实现有组织的提供服务的能力。
CBM通过对企业的业务组件化建模,形成企业业务架构的顶层视图,在一张图上,直观显现出企业的业务蓝图。
CBM 提供了一个可以推广的框架,用来创造顺应组织战略的的指导方向。同时企业也可以通过CBM建立了基于SOA 的规划的方向,为实施 SOA 奠定基础。
CBM通过设计组织的未来形式,推动企业内部和外部向专业化发展。这个过程包括三个方面:第一,通过分析业务和市场环境,得出现有公司的业务组件整体视图;第二,在不断变化的环境中,按照迁移规划方案向专业化方面发展;第三,促使组织、基础设施向组件化的企业方向不断优化。
CBM采用二维矩阵的方式(见图1)描述企业能力的顶层图像,明晰业务能力分布的映射网络
图1 组件业务模型(CBM)
CBM的横向是业务能力,即企业创造价值的能力。通过明确不同部门的业务功能、划分边界,确定关系,确保所有工作都有人在做,而且没有人做重复的工作。
CBM的纵向是职能层级,分为战略/引导层、管理/控制层、执行层。战略层主要指战略、总体方向和政策的业务,聚焦于明确战略发展方向,建立总体的方针政策,调配资源、管理和指导各个业务板块。管理层主要指企业的管理活动,如监控、管理例外情况和战术决策等业务,负责把战略落实到运营当中,监控和管理业务指标和企业员工,发挥看管资产和信息的作用。执行层是指具体的业务执行来实现的业务功能,处理业务请求和业务数据,注重作业效率和处理能力。处理各种资产和信息。
三、CBM设计原则
CBM通过横向业务能力和纵向能力层级对企业的所有业务进行矩阵式定义,但是CBM体现的是企业的业务能力专业化整合能力,其划分并没有固定的方法,同一个企业的CBM可能由于关注点不同而呈现完全不同的结果。业务能力的划分需要与企业价值链保持一致,但先后顺序没有绝对定义。业务组件在划分的时候属于哪个维度,业务组件在操作层面需要反应到什么细节程度,也没有绝对的定义。但是企业的业务在CBM中属于哪个职能层级,可以通过以下原则进行确定。
按管理职能和管控权限的划分两个维度来确定。例如企业人力的备案属于执行层,而人力的审批属于管理层。详细判断原则见图2。管理功能的定义见图3。管控权限的定义见图4。
图2 通过管理职能和管控权限划分业务组件能力层级
图3 管理职能定义
图4 管理权限定义
任何一项业务最终都会落实到执行,但每个业务活动根据其不同的活动的类型,所担负的责任也有所不同。对于会产生3-5年的影响的战略活动、战略规划类活动、体系建设类活动应属于战略层级;对于识别类、过程监管类、制定制度或规则类、统计类、考核评价类应属于管理层级;围绕单一事项的任务执行活动应属于执行层级。
四、CBM的作用
首先,传统的企业分成了独立的功能性部门,无法看到跨部门的协作以提供客户价值。CBM用业务组件描述企业业务,能够概括描述企业整体业务及业务间相互关系。采用业务组件的方式描述企业业务,避免了采用流程较为复杂的方式。采用CBM分析企业业务的作用之一见图5。
图5 采用CBM分析企业业务的作用之一
其次,通过CBM对企业的业务进行建模,使企业原有的所有业务系统都下沉,不再有按业务部门建立的多个烟囱式的业务系统的概念,原有的业务系统变化为一个个提供业务组件和服务能力的能力单元。此外,原有的所有业务系统中的组织,人员,权限,流程引擎,安全等公共基础设施全部抽取,放到同一的平台进行管理,业务系统部再单独构建IT公共基础能力设施。
以后对新业务系统的建设变化为一个个新的业务能力单元的建设,以后各个业务部门使用的不是孤立的业务系统,而是按需由业务能力单元组装成的可灵活配置的业务应用。不再有明确的业务系统的边界概念,而只有业务组件和能力组装的概念。采用CBM分析企业业务的作用之二见图6