解耦和最重要的原则就是依赖倒置原则:
高层模块不应该依赖底层模块,他们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
《资本论》中都曾阐释依赖倒转原则——在商品经济的萌芽时期,出现了物物交换。假设你要买一个IPhone,卖IPhone的老板让你拿一头猪跟他换,可是你并没有养猪,你只会编程。所以你找到一位养猪户,说给他做一个养猪的APP来换他一头猪,他说换猪可以,但是得用一条金项链来换——所以这里就出现了一连串的对象依赖,从而造成了严重的耦合灾难。解决这个问题的最好的办法就是,买卖双发都依赖于抽象——也就是货币——来进行交换,这样一来耦合度就大为降低了。
再举一个编程中的依赖倒置的例子。我们知道,在通信中,消息的收发和消息的处理往往密不可分。就一般的通信框架而言,消息的收发通常是已经实现了的,而消息的处理则是需要用户来自定义完成的。先看一个正向依赖的例子:轻量级通信引擎StriveEngine。tcpServerEngine是StriveEngine.dll提供通信引擎,它发布有一个MessageReceived事件。假设我定义了一个CustomizeHandler类来用于消息处理,那么CustomizeHandler的内部需要预定tcpServerEngine的MessageReceived事件,因此customizeHandler依赖于tcpServerEngine,这就是一个普通的依赖关系,也就是高层模块依赖于低层模块。
而ESFramework通信框架则应用了依赖倒转原则。ESFramework定义了一个IcustomizeHandler接口,用户在进行消息处理时,实现该接口,然后将其注入到rapidPassiveEngine客户端通信引擎之中。
class CustomizeHandler: ICustomizeHandler { public void HandleInformation(string sourceUserID, int informationType, byte[] info) { ······ } public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info) { ······ } } IRapidPassiveEngine rapidPassiveEngine = ESPlus.Rapid.RapidEngineFactory.CreatePassiveEngine(); CustomizeHandler customizeHandler = new CustomizeHandler(); rapidPassiveEngine.Initialize(, , , 9000, customizeHandler);
View Code很明显,相比于上一个例子,这里的依赖关系变成了rapidPassiveEngine依赖于customizeHandler,也就是说依赖关系倒置了过来,上层模块不再依赖于底层模块,而是它们共同依赖于抽象。rapidPassiveEngine依赖的是IcustomizeHandler接口类型的参数,customizeHandler同样是以实现的接口的方式依赖于IcustomizeHandler——这就是一个依赖倒置的典范。
·控制反转(Inversion of Control)控制反转跟依赖倒置是如出一辙的两个概念,当存在依赖倒置的时候往往也存在着控制反转。但是控制反转也有自己的独特内涵。
首先我们要区分两个角色,server 跟 Client,也就是服务方和客户方。提供服务端的一方称为服务方,请求服务的一方称为客户方。我们最熟悉的例子就是分布式应用的C/S架构,服务端和客户端。其实除此之外,C/S关系处处可见。比如在TCP/IP协议栈中,我们知道,每层协议为上一层提供服务,那么这里就是一个C/S关系。当我们使用开发框架时,开发框架就是作为服务方,而我们自己编写的业务应用就是客户方。当Client调用server时,这个叫做一般的控制;而当server调用Client时,就是我们所说的控制反转,同时我们也将这个调用称为“回调”。控制反转跟依赖倒置都是一种编程思想,依赖倒置着眼于调用的形式,而控制反转则着眼于程序流程的控制权。一般来说,程序的控制权属于server,而一旦控制权交到Client,就叫控制反转。比如你去下馆子,你是Client餐馆是server。你点菜,餐馆负责做菜,程序流程的控制权属于server;而如果你去自助餐厅,程序流程的控制权就转到Client了,也就是控制反转。
,通过回调用户自定义的通信协议来实现更加灵活的通信。