一个软件系统总是由很多不同功能的子系统组成的,每一个子系统又会采用不同的设计方式,会包含不同的模块,会编写不同的代码,有不同的函数和变量。我们今天的文章将目光放在软件中存在的各种通信上,从宏观的角度看看软件系统。
百度百科对通信的定义是这样的:
通信,指人与人或人与自然之间通过某种行为或媒介进行的信息交流与传递,从广义上指需要信息的双方或多方在不违背各自意愿的情况下采用任意方法,任意媒质,将信息从某方准确安全地传送到另方
我们只看广义上的定义,简单来说就是通信的双方进行信息交换。在我们的软件系统中,存在着各种双方需要进行信息交换的情况。软件的子系统之间需要通信,网络就是我们经常采用的一种媒介,通过网络,我们可以基于某一种协议,实现两个子系统之间的信息交换。但是,网络只是通信媒介的一种,而且广义的通信存在于软件系统的各个方面,而不只是子系统之间。
一个子系统内的模块之间需要通信,这些模块进行通信的方式很多,可以采用网络的方式,也可以采用直接接口调用的方式,模块通过接口调用将参数传递给一个系统,并从该系统获得结果,这本身就是一种通信。基于这种思路,一个类调用另一个类的接口,在面向对象中这叫发送消息,无疑是一种通信;函数中调用另一个函数,通过参数传递来获取结果,这也是通信;我们对变量的赋值和取值,也是一种通信。总而言之,一个软件系统就是软件系统中各个抽象组件通过某些通信方式将其连接起来。
通信遍布于整个软件系统,我们来看看我们有哪些通信方式可以选择。
软件中的通信有同步和异步之分,同步的方式就是我给你发送消息,我要等到你给我答复才会离开;而异步的方式就是你告诉我让我做什么,我来替你做,如果不放心,留下你的联系方式,我会不断告知你做事情的进度(回调或者更高端的订阅)。两种方式各有利弊,同步的方式更加简单直接,但是如果你有很多事情要做,每件事情都采用同步的方式,你就会发现办事效率很慢,各种事情都是排队进行;异步的方式虽然能够让你在同一时间做更多的事情,但是对于异步通信的双方,都会增加设计难度,调用方需要处理各种回调,这会打乱正常的系统流程,就好比无论你在做什么,每天总有很多电话打给你,告知你某些事情的进度,将你一天的活动搞得支离破碎。所以,原则就是能用同步优先使用同步的方式,这样可以减少很多不必要的bug,而不得不使用异步时,就要考虑系统的吞吐量,系统的线程安全性等等,最好能够通过白盒测试来保证系统质量。
软件通信有本地和分离之分,例如函数调用就是本地,而进程间通信就是分离,广义来说,网络通信也属于进程间通信,只是这样的两个进程分离的更远,有可能跨越了物理主机。本地的方式沟通方便,但是会导致系统过大,耦合加大;分离的方式耦合小,但是通信成本较高。本地通信可以理解为一个公司内各个人之间的协作,而分离的通信方式,可以类比于几个公司一起做一件事。实践中建议两种方式相结合,独立的系统和模块之间使用分离的方式,而每个系统内部当然只能采用本地的方式。
软件通信还有直接和间接之分,直接的方式就是干什么都亲力亲为,间接的方式就是寻找代理。本着自己的事情尽可能自己做的态度,优先推荐直接的方式,但是有些事情,我们自己不够专业,就要采用代理的方式,将专业的事情交给专业的人去做。
上面谈到了几种常见的通信方式和实践方式,下面再来说一个问题,通信的耦合性。总的来说,通信就会导致通信双方的耦合,通信的多少,直接反应了通信双方的耦合大小。可是就和人在社会中生活一样,不可避免都要有通信,也就是在软件系统中耦合是不可避免的,那么如何减小耦合呢?很显然,当耦合的确是大问题的时候,我们采用异步,分离和间接的通信方式。这样通信双方不会互相等待,也不会掌握双方过多的信息,比如异步方式,你可以打电话给我,告诉我做什么,进度打电话通知我,双方仅知道对方的电话号码,对于长相,年龄,住址等等,我们没必要知道。但是这会带来一个问题,系统会非常复杂,而且解耦带来的一个问题就是,我们很难知道我们通信的对象到底谁,对于了解代码增加了难度,有一些经常遇到的问题,比如使用了多态,我们找到代码使用的是哪个子类就会有些尴尬;使用了观察者模式,我们找到订阅者有时也会很困难。这些时候,代码一定要添加必要的注释。所以,总的原则是,解耦适可而止,量力而为,只要保证耦合在可接受的范围即可,完全没有耦合是不可实现的。
将软件系统各种组件的联结归为通信,能够方便我们更好的对系统进行设计,这样就可以将组件之间的联结问题转化为选择何种通信方式的问题,方便我们选择不同的框架和包帮助我们实现系统。