那些年,空气中仿佛还能闻到汉唐盛世的余韵,所以你决不容许本身的脸上有油光,时刻保持活力。然而,你必定曾为这些“高深术语”感到过困扰。也许时至今日,你仍对它们只知其一;不知其二。不过就在今天,这一切都将完全改变!我将带领你以一种全新的高清视角进入奇妙的编程世界,领略涵泳在这些“高深术语”中的活泼泼的地气,以及翩跹于青萍之末的云水禅心。html
内聚,通俗的来说,就是本身的东西本身保管,本身的事情本身作。算法
经典理论告诉咱们,程序的两大要素:一个是数据(data),一个是操做(opration)。而 PASCAL之父Nicklaus Wirth则进一步提出了“程序 = 数据结构 + 算法”的著名公式。虽然提法上有所差别,可是其根本内涵倒是一致的,微妙的差异在于,“数据 + 操做”是微观的视域,“数据结构 + 算法”则是中观的视域。而在宏观的视域下,我认为“程序 = 对象 + 消息”。对象是什么?对象就是保管好本身的东西,作好本身的事情的程序模块——这就是内聚!传统的面向过程编程方法因为割裂了数据结构和算法,使得软件的内聚性广泛低迷,曾一度引起了软件危机。试想,你们都本身的东西很差好保管,本身的事情也很差好作,不引起危机才怪呢!固然,对象的内聚只是内聚的一个层次,在不一样的尺度下其实都有内聚的要求,好比方法也要讲内聚,架构也要讲内聚。编程
《周易·彖传》中讲“乾道变化,各正性命,保合太和,乃利贞”,就是要求每个个体因循着各自的禀赋而努力成就各自的品性,而后各自保全,彼此和合,最终达成宇宙的完满状态。《论语·宪问》中,子路问君子。子曰:“修己以敬。”曰:“如斯而已乎?”曰:“修己以安人”,更是明确的教导咱们要不断提升自身的内聚性,最大限度地减小给他人形成的麻烦,从而达到安人、安百姓、安天下的目标。我想,成长的过程就是一个不断提高内聚的过程。“本身的东西本身保管,本身的事情本身作”,这些孩提时代的教诲,放到今天仍能让很多“大人”脸红不已。太多的人保管很差本身的“东西”,保管很差本身的身体,保管很差本身的婚姻,更保管很差本身如蛛丝般震颤飘荡的狂乱的心。至于作好本身的事情,则更是惘然,甚至不少人连本身的事情是什么都搞不清楚,所以浑浑噩噩,饱食终日。内聚,是一个值得咱们好好反思的问题。 设计模式
·依赖·耦合在面向对象编程中,对象自身是内聚的,是保管好本身的数据,完成好本身的操做的,而对外界呈现出本身的状态和行为。可是,没有绝对的自力更生,对外开放也是必要的!一个对象,每每须要跟其余对象打交道,既包括获知其余对象的状态,也包括仰赖其余对象的行为,而一旦这样的事情发生时,咱们便称该对象依赖于另外一对象。只要两个对象之间存在一方依赖一方的关系,那么咱们就称这两个对象之间存在耦合。 好比妈妈和baby,妈妈要随时关注baby的睡、醒、困、哭、尿等等状态,baby则要仰赖妈妈的喂奶、哄睡、换纸尿裤等行为,从程序的意义上说,两者互相依赖,所以也存在耦合。首先要说,耦合是必要的。咱们来看如下这个实验。api
【王阳明与山中之花】网络
因为王阳明这个对象不依赖山花这个对象,又没有其余的方式来获知山花的怒放状态,因此他要么选择不说,要么瞎说,但不说编译是通不过,而瞎说做为王阳明来说也是通不过的,因此这个系统是没法成立的。要想系统成立,必需要这样写:数据结构
public bool AdmireFlowers()
{
return flower.IsBloomed; ;
}
不管这个山花对象是怎么来的,做为参数传入仍是做为属性设置、仍是在内部构造出来,总之,王阳明与山花之间发生了依赖,两者之间产生了耦合。 固然,这是一个很浅显的问题。有趣的是王阳明对此事的见解:“你未看花时,花与你同寂;你来看花,花于你则一时分明起来。可见心外无物!”王阳明讲的是对的!“心外无物”翻译技术语言是这样的:不存在耦合的两个对象必然拿不到对方的引用!架构
·耦合度·解耦和耦合的程度就是耦合度,也就是双方依赖的程度。上文所说的妈妈和baby就是强耦合。而你跟快递小哥之间则是弱耦合。通常来讲耦合度太高并非一件好事。就拿做为IT精英的你来讲吧,上级随时敦促你的工做进度,新手频繁地须要你指导问题,隔三差五还须要参加酒局饭局,而后还要每天看领导的脸色、关注老婆的心情,而后你还要关注代码中的bug 、bug、bug,和需求的变化、变化、变化,都够焦头烂额了,还猝不及防的要关注眼睛、颈椎、前列腺和头发的状态,而后你再炒个股,这些加起来大概就是个强耦合了。从某种意义上来讲,耦合天生就与自由为敌,不管是其余对象依赖于你,仍是你依赖其余对象。好比有人嗜烟、酗酒,你有多依赖它们就有多不自由;好比有人家里生了七八个娃,还有年迈的父母、岳父母,他们有多依赖你,你就有多不自由。因此老子这样讲:“五音使人耳聋,五色使人目盲,驰骋狩猎使人心发狂,可贵之货使人行妨。”卢梭也是不无悲凉的说“人生而自由,却又无往而不在枷锁中”。所以,要想自由,就必需要下降耦合,而这个过程就叫作解耦和。框架
·依赖倒置(Dependence Inversion Principle)解耦和最重要的原则就是依赖倒置原则:数据结构和算法
高层模块不该该依赖底层模块,他们都应该依赖抽象。抽象不该该依赖于细节,细节应该依赖于抽象。
《资本论》中都曾阐释依赖倒转原则——在商品经济的萌芽时期,出现了物物交换。假设你要买一个IPhone,卖IPhone的老板让你拿一头猪跟他换,但是你并无养猪,你只会编程。因此你找到一位养猪户,说给他作一个养猪的APP来换他一头猪,他说换猪能够,可是得用一条金项链来换——因此这里就出现了一连串的对象依赖,从而形成了严重的耦合灾难。解决这个问题的最好的办法就是,买卖双发都依赖于抽象——也就是货币——来进行交换,这样一来耦合度就大为下降了。
再举一个编程中的依赖倒置的例子。咱们知道,在通讯中,消息的收发和消息的处理每每密不可分。就通常的通讯框架而言,消息的收发一般是已经实现了的,而消息的处理则是须要用户来自定义完成的。先看一个正向依赖的例子:轻量级通讯引擎StriveEngine。tcpServerEngine是StriveEngine.dll提供通讯引擎,它发布有一个MessageReceived事件。假设我定义了一个CustomizeHandler类来用于消息处理,那么CustomizeHandler的内部须要预约tcpServerEngine的MessageReceived事件,所以customizeHandler依赖于tcpServerEngine,这就是一个普通的依赖关系,也就是高层模块依赖于低层模块。
而ESFramework通讯框架则应用了依赖倒转原则。ESFramework定义了一个IcustomizeHandler接口,用户在进行消息处理时,实现该接口,而后将其注入到rapidPassiveEngine客户端通讯引擎之中。
很明显,相比于上一个例子,这里的依赖关系变成了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了,也就是控制反转。
控制反转的思想体如今诸多领域。好比事件的发布/ 订阅就是一种控制反转,GOF设计模式中也多处体现了控制反转,好比典型的模板方法模式等。而开发框架则是控制反转思想应用的集中体现。好比以前所举的ESFramework通讯框架的例子,通讯引擎回调用户自定义的消息处理器,这就是一个控制反转。以及ESFramework回调用户自定义的群组关系和好友关系,回调用户自定义的用户管理器以管理在线用户相关状态,回调用户自定义的登录验证处理,等等不一而足。再好比与ESFramework一脉相承的轻量级通讯引擎StriveEngine,经过回调用户自定义的通讯协议来实现更加灵活的通讯。
由此咱们也能够总结出开发框架与类库的区别:使用开发框架时,框架掌握程序流程的控制权,而使用类库时,则是应用程序掌握程序流程的控制权。或者说,使用框架时,程序的主循环位于框架中,而使用类库时,程序的主循环位于应用程序之中。框架会回调应用程序,而类库则不会回调应用程序。ESFramework和StriveEngine中最主要的对象都以engine来命名,咱们也能够看出框架对于程序主循环的控制——它会为你把握方向、眼看前方、轻松驾驭!
·依赖注入(Dependency Injection)依赖注入与依赖倒置、控制反转的关系仍旧是一本万殊。依赖注入,就其广义而言,便是经过“注入”的方式,来得到依赖。咱们知道,A对象依赖于B对象,等价于A对象内部存在对B对象的“调用”,而前提是A对象内部拿到了B对象的引用。B对象的引用的来源无非有如下几种:A对象内部建立(不管是做为字段仍是做为临时变量)、构造器注入、属性注入、方法注入。后面三种方式统称为“依赖注入”,而第一种方式我也生造了一个名词,称为“依赖内生”,两者根本的差别即在于,我所依赖的对象的建立工做是否由我本身来完成。固然,这个是广义的依赖注入的概念,而咱们通常不会这样来使用。咱们一般使用的,是依赖注入的狭义的概念。不过,直接陈述其定义可能会过于诘屈聱牙,咱们仍是从具体的例子来看。
好比OMCS网络语音视频框架,它实现了多媒体设备(麦克风、摄像头、桌面、电子白板)的采集、编码、网络传送、解码、播放(或显示)等相关的一整套流程,能够快速地开发出视频聊天系统、视频会议系统、远程医疗系统、远程教育系统、网络监控系统等等基于网络多媒体的应用系统。然而,OMCS直接支持的是通用的语音视频设备,而在某些系统中,须要使用网络摄像头或者特殊的视频采集卡做为视频源,或者其它的声音采集设备做为音频源,OMCS则提供了扩展接口——用户本身实现这个扩展的接口,而后以“依赖注入”的方式将对象实例注入到OMCS中,从而完成对音、视频设备的扩展。
“依赖注入”经常用于扩展,尤为是在开发框架的设计中。从某种意义上来讲,任何开发框架,天生都是不完整的应用程序。所以,一个优秀的开发框架,不只要让开发者可以重用这些久经考验的的卓越的解决方案,也要让开发者可以向框架中插入自定义的业务逻辑,从而灵活自由地适应特定的业务场景的须要——也就是说要具有良好的可扩展性。好比上面提到的OMCS网络语音视频框架可应用于音、视频聊天系统、视频会议系统、远程医疗系统、远程教育系统、网络监控系统等等基于网络多媒体的应用系统;以及ESFramework通讯框架可以应用于即时通信系统,大型多人在线游戏、在线网页游戏、文件传送系统、数据采集系统、分布式OA系统等任何须要分布式通讯的软件系统中——这种良好的扩展性都与“依赖注入”的使用密不可分!
·面向接口编程谈到最后,“面向接口编程”已是呼之欲出。不管是依赖倒置、控制反转、仍是依赖注入,都已经蕴含着“面向接口编程”的思想。面向接口,就意味着面向抽象。做为哲学范畴而言,规定性少称为抽象,规定性多称为具体。而接口,就是程序中的一种典型的“抽象”的形式。面向抽象,就意味着面向事物的本质规定性,摆脱感性杂多的牵绊,从而把握住“必然”——而这自己就意味着自由,由于自由就是对必然的认识。
也许以上的这段论述太过“哲学”,可是“一本之理”与“万殊之理”自己就“体用不二”——总结来看,依赖倒置、控制反转、依赖注入都围绕着“解耦和”的问题,而同时自始至终又都是“面向接口编程”的方法——所以,“面向接口编程”天生就是“解耦和”的好办法。由此也印证了从“抽象”到“自由”的这一段范畴的辩证衍化。
“面向对象”与“面向接口”并不是两种不一样的方法学,“面向接口”实际上是“面向对象”的内在要求,是其一部份内涵的集中表述。咱们对于理想软件的期待常被归纳为“高内聚,低耦合”,这也是整个现代软件开发方法学所追求的目标。面向对象方法学做为现代软件开发方法学的表明,自己就蕴含着“高内聚,低耦合”的思想精髓,从这个意义上来讲,“面向对象”这个表述更加侧重于“高内聚”,“面向接口”的表述则更加侧重于“低耦合”——不过是同一事物的不一样侧面罢了。
除此以外,咱们也能从“面向接口编程”的思想中获得“世俗”的启迪——《论语》里面讲,不患无位,患因此立;不患人之不己知,患其不能也——就是教导咱们要面向“我有没有的本事?”、“我有没有能力?”这样的接口,而不是面向“我有没有搞到位子?”、“别人了不了解我?”这样的具体。依我看,这是莫大的教诲!