引用的必要
一個文件實現一個複雜功能的時代已經過去了,甚至一個工程實現一個項目都逐漸被淘汰。
限於層級的劃分、項目的管理、功能的擴展等因素,在程序的設計之初,就有了引用的概念。
使用既定的方式,去使用已經完成的功能或者對象。
部門間的流轉、模塊間的導入都是如此,僅僅關注於自身的邏輯管理,而其他部分只關注接口。
諸多的編程語言中,基本的共同點,就是包、模塊的引入。
Go
語言中,方法間存在兩種引用:值傳遞和引用傳遞。一個大對象,佔據內存爲
S
,如果是值傳遞,經過n
個操作方法,內存佔用爲。但是,如果使用地址傳遞,也就是地址引用,消耗內存爲。
不論是功能還是其他方面,引用都是大有用處,也無處不在。
奇妙的對象
面向對象的模式的優缺點不用再討論,我也沒能力置喙,只是說說它和引用的奇妙關聯。
面向對象的封裝模式,更提高了內聚,但是引出一個本質問題:多層引用。
我們通過模塊引入一個類,實例化類之後通過對象引用方法。
尤其重要的是,我們更依賴於對象的引用,這就把引用的本質,或者面向對象的本質給顯露出來。
普遍的引用,通過方法的傳遞,或者模塊的導入即可完成,但是面向對象中間還夾雜了實例化這個步驟。
總結一下,面向對象的幾個繁瑣點
- 個體依賴性
模塊可以直接引用、方法可以直接調用,但是面向對象中依賴的並不是直接的外部接口,而是依賴的具體對象。
即使是同一個類,由於參數的不同,相同的方法之間的差異性也非常巨大,不存直接引用這種說法。
- 引入複雜性
由於前述的一點,當涉及多個對象組合的功能的時候,如何引入這些依賴的對象。
加上其他模塊對於基礎對象的相同的引入,不可能由某一個特定的使用類來具體的實例化。
同時,不管由構造器還是外部方法進行設置,都顯得十分笨拙。
或者還有其他原因,但都大同小異,那就是:面向對象帶來了引入的動態性質。
之前的引入,都是靜態的,但是基於對象而非類的依賴,尤其是對象的封裝特性,我們不可能做到統一的引入。
勢必,要有那麼一個東西,支持、簡化、統一我們的對象的引用。
注入(DI)
模塊和方法都是靜態的,對其接口即可,對象確實動態的,必須指定個體。
而且由於可能的多方面的引用,無法交給任何一個調用類來進行實例化管理,必須存在一箇中心的容器,去維護這些基礎的素材。
最後,讓對象,和其他語言中的模塊一樣,成爲一種靜態的,可無歧義的直接進行引用。
每種語言,支持的都是基礎的靜態引用,而java
中的辦法,或者說spring
的方案是:使用一個對象容器,來進行維護管理。
然後通過注入
這種方式,來模仿導入的過程。也即是,你說你需要,我一個個給你們填充。
看看一個文件中高達十幾個的類引用,想到一個個的實例化的恐懼,想到幾十個類之間的交叉引用,慶幸。
幾十個的get/set
,誰都會對代碼喪失興趣。
控制(IOC)
然後,接踵而來的更重要的問題,管理的對象哪裏來。
如果說藉助容器,我們自己去挨個註冊,自己去實例化,不說多麼無味和重複,只是,有必要麼。
通過反射,或者其他什麼辦法,讓容器自主的能夠去創建、管理對象。
減少失誤,提高開發進度,尤其死板的嚴格的管控條例,獲得更準確的服務保障,這就是IOC
的極大優勢。
切面(AOP)
不得不說,基於對象引用的繁雜操作讓人厭煩,但是,其所帶來的操作空間的確巨大。
對於一個基礎的功能,如果我們想要稍微修飾一下,就會顯得十分雞肋
- 方法包裝
可以寫方法進行包裝,但是卻可能時常更換,浪費人力,切效用不大
- 操作包裹
操作時候去修飾,沖淡邏輯的緊密性,分散精力,冗餘垃圾代碼
不論採取什麼方式都顯得雞肋----但是,java
中這種問題不復存在。
如上,雞肋讓我們不論從模塊還是程序的角度,都無法保證純淨。
多出來的中間過程,讓我們可以盡情的去定製所需要的即將引用的對象及其方法。
在每個階段都能夠保證自身的純淨、邏輯的聚合。
其中的從加載到容器的階段,正是切面(定製)的活動空間。
由於
jvm
是基於class
的中間語言的平臺,從java
到class
的步驟,也是這一步的領域。因此,切面在於已經加載對象的重新定製,還有
javaassist
等基於字節碼的包裝功能。不論是加載即包裝,還是引用即包裝,都是對象引用所提供的定製化的操作空間。
忽略具體細節,把class
字節碼文件等同於java
的內容,就可以統一理解。
注意:
class
的直接定製,由於不存在更多的聯結特性,效能優於基於對象的包裝方案。
操作的空間
如果非直接的流程,在中間的傳輸過程中,就可能存在更多可能的操作,提供更多的使用空間。
ribbon
的負載均衡,正是由於註冊中心的非直接調用,才得以實現。
微服務中的小模塊,管理和調用,正如java
中的對象引用的管理,容器中心也就變成了服務中心。
簡單的引用,其本質,天差地別。