文档章节

模块化与解耦

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:17
字数 3190
阅读 6
收藏 0
点赞 0
评论 0

注意:本文并非原创文章,转载自(原创):https://blog.cnbluebox.com/blog/2015/11/28/module-and-decoupling/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
前些天读到这篇文章,印象中感觉这篇文章简短精炼,所以今天重新翻出来分享给大家!

简述

本文主要讲述了在iOS开发过程中,模块化工程架构的一种组织方式,本文主要讲述基于cocoapods来做模块化的方案,详细讲述了iOS开发怎么进行模块划分的内容,主要会在以下方面做阐述:

  • 为什么要做模块化
  • 模块设计原则
  • 模块化开发有哪些优点和缺点
  • 解耦与通信

1.为什么要做模块化?

我们都知道最基本的代码设计原则:“Don’t repeat yourself!”,每一个工程都会有自己的架构,即使你是刚入门的开发者,写几天代码也会发现要把一些常用到的重复代码单独拿出来放在一个叫common的地方,实现代码复用。这样看来每个开发者其实都或多或少的做过架构方面的事情,每个团队至少有1~2个人在做这样的事情。

说到app代码架构,记得Samurai的开发者郭虹宇在群里说过这段精辟的话,引用一下:
一派是说app开发并不需要什么狗P架构,第二派说我们有自己NB的架构,第三派说只要模块化够好,每个模块应该有自己的架构。

这三个观点的出发点,我觉得也比较好理解,第一种应该是一些个人开发者,个人能力很强,经常一个人很快搞出来一个app,他的映像中不需要弄太多的框框框住自己,但是其实他也是有一套自己的架构的。第二派应该是一些公司或者大公司,有一套NB的架构对于团队的意义就比较大了,可以保证稳定迭代,保证规范和持久可维护性。第三派应该是BAT这样的有很多BU的超级公司,或者一些先进的开源开发者们,模块化能够更好的实现跨app的代码和功能的复用, 能够更好的共享资源,避免重复造轮子。

那么为什么要做模块化?已经很明显了,模块化的代码框架最屌,不信,看看苹果的框架怎么做的,你就明白了。

2. 模块设计原则

既然模块化最屌,那怎么才能做好project的模块化拆分呢,哪些代码应该被放到一个模块?这里分享一些我的经验。
越底层的模块,应该越稳定,越抽象,越具有高复用度。

这一点,目测大家应该比较认同,越是底层的SDK,就应该越稳定,稳定的最直观表现就是API很久都不用变化,所有的变化因子不要暴露出来,避免传递给依赖它的模块。但是要做到设计一套API很久都不用改变,那么就需要设计的时候能越抽象, 即需要我们抽象总结的能力。

稳定性 还有一个特点就是会传递,比如 B 模块依赖了 A 模块,如果 B 模块很稳定,但是 A 模块不稳定,那么B模块也会变的不稳定了,因此下一个原则:
不要让稳定的模块依赖不稳定的模块, 减少依赖。

既然上面说最好不要依赖,但是我发现我的 B 模块的确依赖了 A 模块里面不可或缺的代码怎么办? 假设依赖的代码段为 x , 现在来看x的特性, 如果X是一个可能高复用的代码段,那么无妨把x从 A 模块里面拿出来,单做成一个模块 X, 那么 B 模块依赖 X 模块就好了;灵一种情况,x是一个方法或函数,而且不太适合单做成一个模块,所以那就在B模块里面拷贝一份 x 代码就ok了,因为这样可以保证模块的 稳定性自完备性
如果上面两种方法都不太合适,我们会在后面解耦里面讲到如何解耦。
提升模块的复用度,自完备性有时候要优于代码复用。

什么是自完备性,就是尽可能的依赖少的模块来达到代码可复用。
举个例子,我有个模块 Utils 里面放了大量的category工具方法等,在日常UI产品开发中,依赖这个Utils会很方便,但是我现在要写一个比较基础的模块,应该就要求复用度更高一些,这个时候需要用到Utils里面的几个方法,那这个时候还适合直接依赖Utils吗,当然不合适了,这与我们上面的设计原则相悖了啊,因此我们这时候为了这个模块的自完备性,就可以重新实现下这几个方法,而不是依赖Utils模块。
每个模块只做好一件事情,不要让Common出现。

模块化结构是让工程结构更清晰,每个模块都只做一件事情,都有自己的一个命名,这样这个模块才能良性发展, 但是这个名字千万不要再叫Common了,试想下你有没有做过这样的事情:“哎呀,这块代码放哪都不太合适,放Common吧”, 日久以后,这个Common就变成了毒瘤,大家都依赖它,还一堆不相关的代码,这个Common模块就是我们设计原则第一点的反面教材: “非常不稳定,大量依赖,全是耦合,整个模块无法复用到其他app”, 所以删掉工程里面的Common吧,再遇到不知道放哪的代码,就要好好思考模块的设计,再不行如果具有可复用性就单建一个模块吧,为什么不可以呢?
按照你架构的层数从上到下依赖,不要出现下层模块依赖上层模块的现象。
业务模块之间也尽量不要耦合。

3. 模块化开发有哪些优点和缺点

优点: 1、不只提高了代码的复用度,还可以实现真正的功能复用,比如同样的功能模块如果实现了自完备性,可以在多个app中复用 2、业务隔离,跨团队开发代码控制和版本风险控制的实现 3、模块化对代码的封装性、合理性都有一定的要求,提升开发同学的设计能力。

缺点:模块化当然也有它的缺点: 1、入门门槛较高,新手入门需要的成本也更高 2、工具的使用成本,团队间和模块间的配合成本升高,开发效率短期会降低。

但是从长期的影响来说,带来的好处远大于坏处的,因此模块化仍然是最佳的架构选择。

4. 解耦与通信

我先说说为什么要解耦吧,模块化并不是说你把工程的代码拆分成 50 个 pod 或者framework就算完事了,要实现模块之间真正的解耦才算真正的模块化,否则如果模块之间还都是互相调用代码,循环依赖,那么和原本放文件夹里面没啥两样。那么什么是模块间的解耦呢?
模块解耦的目标就是, 在基于模块设计原则上, 让模块之间没有循环依赖, 让业务模块之间解除依赖。

4.1 公共模块下沉

这块其实还是讲的模块设计,一个工程的架构可能会分为很多层,然而在开发的过程中,很容易有人不注意让应该处于较底层的模块依赖了上层的模块,这种情况下应该对模块的设计进行改造实现单向依赖。

比如一个常见的普遍的例子: 一个公共的WebView模块,里面可能有WebViewController的基类,然后还有JSBridge的服务,如果设计的时候没有注意,很容易在开发过程中,这个模块被塞入大量的其他业务代码,依赖了一大堆业务模块,因为经常注册JSBridge服务需要跟业务耦合。

这个时候怎么做呢,首先我们要思考WebView模块的定位,从更全局的角度思考,每个app的架构应该都需要这样一个模块,那么我们完全可以把这个模块单独拎出来下沉为基础模块,这个时候的解耦就需要你对WebView模块做出一些设计,添加一些注册型Api,修改JSBridge的服务为可以通过注册的方式添加逻辑,这样来实现与业务解耦,业务完全可以把与自己业务相关的代码放在自己的模块里面,然后通过你设计的Api注册到WebView模块中。

4.2 面向接口调用

虽然说公共模块可以通过架构设计来避免耦合业务,但是业务模块之间还是会有耦合的啊,而且这种情况是最多的,比如页面跳转啊,数据传递啊,这些情况前面的方法已经不够用了。那如何解耦不同业务模块之间的代码调用呢?

那就是面向接口调用,我们知道只要直接引用代码,就会有依赖,比如:

// A 模块
- (void)getSomeDataFromB { 
    B.getSomeData();
}

// B 模块
- (void)getSomeData { 
    return self.data;
}

那么我们可以实现一个 getSomeDataFromB 的接口,让 A 只依赖这个接口,而 B 来实现这个接口,这样就实现了 A 与 B 的解耦。

// 接口
@protocol BService <NSObject>
- (void)getSomeData;
@end

// A 模块, 只依赖接口
- (void)getSomeDataFromB { 
    id b = findService(@protocol(BService)); 
    b.getSomeData;
}

// B 模块,实现BService接口
@interface B : NSObject <BService>
- (void)getSomeData {
    return self.data;
}

这样就可以实现了即满足了模块之间调用,也实现了解耦。

优点:

  • 1、接口类似代码,可以非常灵活的定义函数和回调等。

缺点:

  • 1、接口定义文件需要放在一个模块以供依赖,但是这个模块不回贡献代码,所以还好。
  • 2、使用较为麻烦,每各调用都需要定义一个service,并实现, 对于一些具有普适性规律的场景不太合适,比如页面统一跳转。

4.3 面向协议调用

面向接口调用的缺点导致并不能满足所有的需求,也解耦的不够彻底,那么终极手段就是通过定义一套协议来实现模块间的通信,协议现成的,那就是URL跳转协议,基本满足需要,简单易上手,基本上现在很多的App架构里面都会有“统一跳转” 这一套东西的,这个不光是对模块解耦有帮助,对于统一化运营都是有极好的帮助的,比如app里面的任何页面,或者任何操作都是通过一个URL来唤起的话,这样是不是就把各个复杂的业务之间解耦了呢,通信都使用URL

5. 源码推荐

说了这么多,也要放点干货吧,下面给出2个库的介绍,对你模块化的进程希望有帮助。
1、 JLRoutes 是一个URL跳转协议支持的库,跟我想要的简直很契合,强烈推荐。
2、 我自己写的一个解耦框架 AppLord. 简单介绍一下几个概念。

  • Module 是负责管理启动模块的工具,可以帮助你把AppDelegate里面一坨初始化的代码分别放到不同的module里面去
  • Service 就是对上面 4.2 中面向接口解耦方式的一种封装
  • Task, 全局的后台任务管理器,有时候一些不知道放哪的任务执行可以塞进去

文/VV木公子(简书作者)
PS:如非特别说明,所有文章均为原创作品,著作权归作者所有,转载转载请联系作者获得授权,并注明出处,所有打赏均归本人所有!

如果您是iOS开发者,或者对本篇文章感兴趣,请关注本人,后续会更新更多相关文章!敬请期待!

© 著作权归作者所有

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2139
码字总数 82983
作品 0
程序员
iOS 灵活的 模块化/组件化 工具与规范 Lotusoot 解说

iOS 灵活的 模块化/组件化 工具与规范 Lotusoot 解说 Harries Blog™2017-12-134 阅读 IOS技术ACEAndroidAppcatCocoaPodsgitGitHub 开篇 上一篇《 href="https://link.jianshu.com/?t=http:/......

Harries Blog™ ⋅ 2017/12/13 ⋅ 0

仿全民TV--likequanmintv

基于 MVP 的 Retrofit2(okhttp3)+rxjava+dagger2+greendao+glide+rtmp 直播。该项目系仿全民TV,属于独立开发者作品。 直播播放器(使用模块化开发方式)。基于模块规模合理采用 MVP、MVC 优雅...

MarshonChen ⋅ 2016/12/02 ⋅ 0

在组件化中使用Dagger2遇到的问题

该文参考@格竹子发布的JIMU组件化方案和Dagger2在模块化中的应用 问题 从去年11月开始看到@格竹子发布的组件化方案开始重构公司项目。那么首先,组件化需要解决的问题一般来说有以下几点: ...

EingShaw ⋅ 03/29 ⋅ 0

QUANTAXIS —— 面向中小型团队的量化金融策略框架

QUANTAXIS 量化金融策略框架,是一个面向中小型策略团队的量化分析解决方案。通过高度解耦的模块化以及标准化协议,可以快速的实现面向场景的定制化解决方案。QUANTAXIS是一个渐进式的开放式框...

王练 ⋅ 2017/12/18 ⋅ 0

组件化 Javascript 开发框架--Transformers

Transformers 是一套基于 Javascript(JS) 的已彻底组件化与模块化的开发框架,与 Web Components 理念一致。框架主要关注组件路由、组件消息传递和组件异步加载等,其中要解决的核心问题是组...

CodeIgniter ⋅ 2014/10/09 ⋅ 0

漫谈SOA(面向服务架构)

面向服务架构的思想在整个软件的架构中已经不是什么新鲜的东西。我简单的认为服务化是模块化的延伸,所以服务化有着和模块化类似的优点和缺点。这里不再讨论这些服务定义服务与服务之间的通信...

huojiao2006 ⋅ 2017/03/06 ⋅ 0

PacketInterceptor的妙用

of的设计是模块化,插件化的,尽可能的解耦各功能之间的关联,通过各种XMPPServer实例来协调各种资源,这就非常巧妙和轻盈。其中又有很多功能组件非常灵活,比如:PacketInterceptor。 顾名思...

今幕明 ⋅ 2014/04/02 ⋅ 0

OpenDigg iOS开源项目月报201704

由OpenDigg 出品的iOS开源项目月报第一期来啦。我们的iOS开源月报集合了OpenDigg一个月来新收录的优质的iOS开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等。 Transition 构建iOS...

OpenDigg ⋅ 2017/04/13 ⋅ 0

iOS 模块化解耦方案--DecouplingKit

Podfile 平台:IOS,' 7.0 ' 荚' DecouplingKit ','〜> 0.0.2 ' DecouplingKit是一个用于模块之间解耦的方案。 当应用团队的人数增长到一定人数之后会分出业务线,这样就会就行模块化工作来...

flyicarus ⋅ 2017/03/11 ⋅ 0

寻找Android架构之路

Android开发过程中,我们或许见过这样的现象: 1. 上帝类频频出现,有些类可能包含多个功能模块的代码 2. 臃肿类数不胜数, 很多类里面可能少则1000行,多则几千行的代码 3. 不同功能混杂,类...

亓春杰 ⋅ 01/05 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

内核线程、轻量级进程、用户线程

线程与进程概念 在现代操作系统中,进程支持多线程。 进程是资源管理的最小单元; 线程是程序执行的最小单元。 即线程作为调度和分配的基本单位,进程作为资源分配的基本单位 一个进程的组成...

117 ⋅ 19分钟前 ⋅ 0

elasticsearch2.4.6升级为elasticsearch-5.5.0的经历

将elasticsearch-5.5.0 中的配置 path.data 指向原来的数据路径 即 path.data: /usr/local/src/elasticsearch-2.4.6/data 注意: elasticsearch-5.5.0 需要将jdk版本升级到1.8...

晨猫 ⋅ 20分钟前 ⋅ 1

lvm讲解 磁盘故障小案例

1

oschina130111 ⋅ 24分钟前 ⋅ 0

那些提升开发人员工作效率的在线工具

本文转载自公众号 Hollis 作为一个Java开发人员,经常要和各种各样的工具打交道,除了我们常用的IDE工具以外,其实还有很多工具是我们在日常开发及学习过程中要经常使用到的。 Hollis偏爱使用...

时刻在奔跑 ⋅ 36分钟前 ⋅ 0

restful风格 实现DELETE PUT请求 的web.xml的配置

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframe......

泉天下 ⋅ 41分钟前 ⋅ 0

Shell数组

Shell数组 Shell在编程方面比Windows批处理强大很多,无论是在循环、运算。 bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数...

蜗牛奔跑 ⋅ 51分钟前 ⋅ 0

nmap为了开发方便 可以做简单的修改

因为nmap扫描是默认使用的是nse脚本,但是在开发的过程中需要修改后缀(主要是因为后缀为lua才能显示高亮,所以这里用一个取巧的办法) nse_main.lua文件中我们找到如下代码 local t, path = cn...

超级大黑猫 ⋅ 55分钟前 ⋅ 0

springmvc获取axios数据为null情况

场景:前端用了vue没有用ajax与后台通信,用了axios,但是在代码运行过程中发现axios传递到后台的值接受到数据为null。 问题原因:此处的问题在与axios返回给后台的数据为json类型的,后台接...

王子城 ⋅ 57分钟前 ⋅ 0

hadoop技术入门学习之发行版选择

经常会看到这样的问题:零基础学习hadoop难不难?有的人回答说:零基础学习hadoop,没有想象的那么难,也没有想象的那么容易。看到这样的答案不免觉得有些尴尬,这个问题算是白问了,因为这个...

左手的倒影 ⋅ 57分钟前 ⋅ 0

806. Number of Lines To Write String - LeetCode

Question 806. Number of Lines To Write String Solution 思路:注意一点,如果a长度为4,当前行已经用了98个单元,要另起一行。 Java实现: public int[] numberOfLines(int[] widths, Str...

yysue ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部