改系列文章是2016年折腾的一个总结,对于这一年中思考和解决的一些问题作一些梳理和总结。javascript
前两篇文章主要是说了业务逻辑接口还有模块化的事情。随着系统内部逻辑单元(多是模块,也多是为了解耦拆解出来用来承载职责的类等常见的实现)的增多。势必会引入另外的一个问题,就是逻辑单元之间的交互增长和逻辑单元之间通讯成本的提升。在iOS架构设计系列之解耦的尝试之变异的MVVM,一文中咱们在将整个业务逻辑层从MCV向MVVM演变的时候也遇到了这个问题,当时是本着做孽本身造轮子的心态,经过构建EventBus组件来解决。一样,针对于逻辑单元之间通讯成本增长的问题,也须要寻找一个合适的解决方案。html
在ios多模块管理一文中,描述一种进行系统模块拆解和管理的思路。将职能不一样的业务,拆解成了独立的模块。并且每一个模块经过代码隔离,作到了互相之间影响的最小化。可是他们之间怎么交互呢?换种说法就是业务模块应该暴漏什么样的外部接口,以方便其余业务模块来调用?java
在终端上业务逻辑主要是围绕着界面展开的。在iOS中的表现就是各式各样的ViewController。而在以往的编码实践中,所谓业务模块间的交互就是VC之间的相互调用。咱们常见的是这样的:linux
UIViewController* aVC = [UIViewController new];
//配置aVC须要参数
....
[self.navigationController pushViewController:aVC animated:YES];复制代码
或者这样的ios
UIViewController* aVC = [UIViewController new];
//配置aVC须要的参数
...
[self presentViewController:aVC animated:YES];复制代码
咱们经过对于指定业务模块的代码级引用来调用对方的服务。而这种依赖属于接口依赖,稍微符合接口隔离的设计。但不是一个符合迪米特法则(最小知识法则)的设计。调用方因为对于服务提供方有接口依赖,于是就形成了如下的潜在问题:c++
这些问题,咱们经过代码级别的模块隔离基本上解决了。正如前文所说,你要真正把模块之间的交互影响下降的最小,最好的解决方案就是建造『信息孤岛』,而信息孤岛就会形成模块之间『鸡犬之声相闻老死不相往来』,这也非我等所愿。他们之间还要保持一个最小的通讯,来完成服务的调用。这样咱们在代码隔离以后,就要解决两个问题:git
解决这两个问题,咱们首先要说一个观点就是机制与策略分离。咱们但愿设计的是一整套可以知足上述要求的协议,其次才是实现,最后才是在咱们的APP中的具体应用。这也是我这一年来的一个很是重要的总结。而且在逐渐开源出来的一些库中也体现着这个设计。具体说一下,所谓机制便是抽象出来的规则,好比:github
f(x)=x^2 x属于R复制代码
所谓策略便是在具体场景中的应用,好比当x=2的时候:windows
f(2)=4 x=2复制代码
很明显刚才说的三个层次中协议与实现作成了一个机制与策略分离。而实现与应用又组成了另外的一个机制与策略分离。我比较喜欢这种嵌套的解决方案,你解决了一个通用性的问题,而后嵌套使用,就可以解决更多的问题,只须要付出少许的思惟成本。网络
协议是问题解决方案的描述,或者说要解决这个问题你们都应该遵照的规则。就像网络的tcp协议,你要基于tcp通讯你就须要遵循这个协议。
实现是针对于某类环境的实施方案,好比linux上对于TCP的实现还有windows上对于TCP的实现。虽然都是一个协议,可是你们的实现方式不同,有基于c写的,有基于c++写的.
而应用是真对具体的问题域提出的实施方案,好比咱们作了一个哟呵校园的聊天软件使用了tcp进行socket通讯。
那咱们首先要作的就是针对模块间通讯问题构思一个协议。一个为了解决模块间通讯问题你们都遵照的规则。其实关于这个问题在今年下半年,业界飘来一股router风。你们都在模块化以后的通讯问题上做出了不一样的尝试。并且甚至为此进行了一场博客间的辩论。仔细分析一下,就能发现你们虽各有意见,可是基本上都赞成使用URL的方案来解决这个问题。所争执的不一样在于实现方案上的差别。而此处的URL正是咱们所谓的协议部分。
统一资源定位符(或称统一资源定位器/定位地址、URL地址等[1],英语:Uniform / Universal Resource Locator,常缩写为URL),有时也被俗称为网页地址(网址)。如同在网络上的门牌,是因特网上标准的资源的地址(Address)。它最初是由蒂姆·伯纳斯-李发明用来做为万维网的地址。如今它已经被万维网联盟编制为因特网标准RFC 1738。
为什么如此?
回到最开始咱们描述的问题中第一点: 模块发现。其实也就是模块这种资源的定位问题,这个和URL设计的初衷是不谋而合的。URL整套的设计思路就是在总体的互联网中解决信息孤岛,让各个信息孤岛之间可以进行资源发现和资源调用而设计。而咱们目前所要处理的模块间通讯问题,实际上是这个宏大问题域的一个子集。于是选用URL协议,是一个很是瓜熟蒂落的事情。另一点,这里真心不必从新造一个相似于URL的协议的轮子出来的。URL协议中可以很是完美的解决这个问题。
其实业界这个route的实现已经有千千万万了,为啥我还要再写一个?一个是由于原有的一些库的模型和我所想象的不吻合,一个是由于我实现不想削足适履去适配他们的模型。因此本着造轮子的做孽心态仍是本身写。其实也不是很是复杂。
URL协议解决了模块发现的问题,可是是个静态的txt,并不具有exe的能力。咱们能够经过定义一个相似于:
yoho://innerfuction/viewcontroller/showuserinfo?uid=22&xx=33复制代码
来让一个模块对外宣称支持显示用户详细信息的服务。可是咱们要如何使用这个服务呢?不少Route的实现是经过URL直接将对应的ViewController返回,而后由调用方再去调用接口配置ViewController,然后调用方进行push或者present。而我认为这种方式不是很合理,作为调用发应该尽量少的知道服务提供方的信息。服务怎么被弹起,应该是由服务方本身决定的,而不是调用方。最好只知道一个URL还有支持什么样的服务就行了,最好能把交互接口精简、精简、再精简。
而思考了一下不少route库之因此没可以作到模块只对外暴漏URL就能够的一个很重要的缘由,就是在ViewController被弹出的时候,iOS须要一个调起的ViewController
UIViewController* aVC = [UIViewController new];
//配置aVC须要参数
....
[self.navigationController pushViewController:aVC animated:YES];复制代码
就是必须知道当前界面是在哪里,你才能去push下一个界面。只有知道这个self.navigationController上下文信息才行。因此不少事情只能在调用方来处理。我以为这种方式制约着被调用方须要拿到服务提供方的一个实例才行。每个问题背后都有一个解决方案。因而我在本身造的轮子中使用了全局UI堆栈的方式解决了这个问题。
经过构造了被调用的上下文信息类DZURLRequestContext,用于携带调用方的上下文信息来解决这个问题。上下文信息中携带了当前UI的堆栈信息,可以方便定位用哪一个VC作为起点,来弹出下一个页面。固然使用这个context还有另一个缘由就是由于URL中可以传输的参数类型是受限的,只能传输NSString类型,对于一个实例则不能传输。为了传输实例参数也须要这样的一个context环境。
这样在调用一个页面的服务的时候,就可以作到以下所示极致简单:
[[DZURLRoute defaultRoute] routeURL:DZURLRouteQueryLink(kYHURLSacnQRCode, @{})];复制代码
固然该库首先是解决了经过URL调用的问题,然后才是上面说的这些优化问题。同时,也针对不少不一样的应用场景提供了解决方案。更加具体的信息能够参考DZURLRoute。
而具体的应用问题,就是APP内部本身的事情了,不展开叙述。基本上都是调用库接口的事情,没有太多的表述价值。
作个预告吧,也算是对本身的一个敦促,下一篇说一下在DZURLRoute中关于UI堆栈的问题是怎么解决的。
欢迎关注iOS开发公共帐号 iOS开发知识 :扫描下方二维码关注