1. 程式人生 > >Intellij IDEA的設計和實現使用了哪些設計模式

Intellij IDEA的設計和實現使用了哪些設計模式

ntelliJ IDEA第一版釋出於2001年1月,這是第一款集成了高階程式碼導航和程式碼重構功能的Java IDE。 2009年,JetBrains開源了其社群版。從那時開始,就新出現了許多基於其社群版的IDE,如Google的Android Studio。 本文使用JArchitect作為工具,深入瞭解Intellij IDEA社群版,探索其中使用的一些內部設計決策。 1、模組化 Intellij IDEA以模組化的方式組織,使用了多個專案。最主要的專案是“idea”,“util”專案中實現了工具類,而“openapid”這個專案的jar含有開發Intellij IDEA外掛所需的型別。 下面列出了Intellij IDEA的專案,以及相關型別的統計資訊。  
每個專案都以模組化的方式組織程式碼,使用多個包並將相同特性的包組織在一起。 基於特性劃分包(package),將程式碼根據其特性放在不同的包裡。即將所有與某個單一特性相關的內容都放在一個單個目錄或包中。這樣就使得所有的包都具有高聚合性和高模組化,把包與包之間的耦合性降到最低,互相依賴非常緊密的內容都放置在一起。 下面是idea專案中一些包的例子,可以看到其中的型別是根據特性分類的。
 
2、Intellij IDEA開發者廣泛使用了GoF設計模式 設計模式是一個軟體工程概念,描述了在軟體設計中許多常見問題的解決方案。GoF模式是其中最著名的一個。 Intellij IDEA開發者使用了擴充套件的GOF模式,下面是一些在程式碼中使用的設計模式。 2.1、工廠模式 使用工廠模式可以分離邏輯並加強聚合性,下面是在程式碼中定義的一些工廠類。  
在Intellij中實現了許多工廠類,下面就是一些繼承自TextEditorHighlihtingPassFactory的工廠類。 圖: 2.2、介面卡模式 介面卡模式是兩個不相容介面之間的橋樑。這種型別的設計模式來自於結構模式,結構模式將兩個互相獨立的介面組合起來。 在Intellij IDEA的程式碼中實現了許多介面卡:
 
2.3、裝飾器模式 裝飾器模式可以用來擴充套件(裝飾)某個物件的功能,而且無需改變其結構。在Intellij IDEA中實現了許多裝飾器。  
2.4 代理模式 代理模式最普通的形式就是一個類,該類作為其他型別的介面。 下面是一個例子,FieldBreakpoint和FrameVariablesTree這兩個類使用了兩個代 理:VirtualMachineProxy和StackFrameProxy。VirtualMachineProxy介面用來替代 VirtualMachineProxyImpl這個實現。然而,與FrameVariablesTree相關的StackFrameProxyImpl 卻不是這樣。也許這裡需要重構一下,移除互相的依賴比較好。
 
2.5、Facade(外觀)模式 Facade模式隱藏了系統的複雜性,向客戶端提供了一套用於訪問系統的介面。下面是Intellij IDEA中實現的CodeStyle Facade。  
2.6 訪問者模式 訪問者模式是用來將物件的結構本身和演算法分離。 下面的程式碼高亮特性就是用訪問者模式實現的。  
2.7、策略模式 在許多情況下,某些類之間區別只在於他們的行為不同。這種情況下,最好的辦法是將不同類中的演算法與類本身分離出來,在執行時讓類選擇不同的演算法。 在Intellij IDEA的原始碼中,許多類都實現了策略設計模式:  
2.8、Builder模式 這種設計模式允許客戶物件構建一個複雜的物件。Intellij IDEA的原始碼中,實現的ConrtolFlowBuilder就是這樣一個builder。 下面列出了一組會被ControlFlowBuilder.build呼叫的方法:  
3、耦合 應用程式的耦合度越低越好,這樣當應用的一部分發生改變時,另一部分受到的影響就不大。從長遠來看,在更改應用的需求時,低耦合可以節省大量的時間、精力和資金。 下面列出了使用介面帶來的3個主要優點:
  • 介面就是定義了一套契約,用來方便重用。如果某個物件實現了一個介面,就表明這個物件遵循這個介面的標準。使用這個物件的物件成為消費者,介面就是這個物件和消費者之間的契約。
  • 介面還提供了某種層次的抽象,讓程式更易理解。介面允許開發者以更一般的方式討論程式碼的行為,而不是一頭扎進程式碼的細節中。
  • 介面讓元件之間的耦合更低,使得介面的消費者不會接觸實現這些介面的類中的內部改動。
Intellij IDEA中定義的許多介面和抽象類都是為了降低耦合。  
下面的Metric View中,藍色區域就是使用了介面的程式碼。  
在Metric View中,程式碼是使用Treemap表示的。Treemap是一種表示以巢狀矩形,按樹的方式組織的資料。樹結構一般用來表示程式碼層次。
  • 專案中含有多個包。
  • 專案中含有多個型別。
  • 型別中含有多個方法和欄位。
Treemap檢視可以很好的表示CQLinq查詢的結果:藍色矩形表示的查詢結果,所以能以可視的方式瞭解查詢中關注的型別。 我們可以檢視所有包中定義的介面和抽象類,方便地表示包中提供的特性。 4、聚合 單一功能原則(Single responsibility principle)規定每個類都應該只有一個單一的功能,即類應該是聚合的。一般LCOM值越高,表示類聚合越低。LCOM有好幾種表示方式。LCOM 表示的是0-1之間的值,而LCOM HS(HS表示的是Henderson-Sellers)表示的值範圍是0-2。LCOM HS值大於1是很危險的事。下面是LCOM的計算方式:
  1. LCOM = 1 – (sum(MF)/M*F)
  2. LCOM HS = (M – sum(MF)/F)(M-1)
複製程式碼



其中:
  • M是類中方法的數量(包括靜態方法和例項方法,其中還包括構造器、屬性設定和獲取方法、新增和移除事件的方法)。
  • F是類中例項欄位的數量。
  • MF是類中訪問特定例項欄位的方法的數量。
  • Sum(MF)是類中所有例項欄位的MF的總和。
這些公式中的基本思想為:完全耦合的類中,所有方法會使用其所有的示例欄位,這意味著sum(MF)=M*F,即LCOM = 0且LCOMHS = 0。 如果LCOMHS的值大於1,就要小心了。  
只有少數型別不是聚合的。 5、多執行緒和併發 為了讓Intellij IDEA具有更好的響應能力,其中建立了許多執行緒來提高使用者體驗。 首先,來搜尋所有以直接和非直接執行緒開始的方法:  
其中併發邏輯分離出來,位於下列的包中:  
為了使用併發開發,這裡使用了JSR166。 下面列出了所有jsr166 jar使用的所有型別:  
6、抽象度與不穩定性圖 這幅圖的思想是,程式中使用的程式碼元素越多,其抽象度就應該越高。換句話說,不應該過多依賴實現,而應該依賴抽象。這裡的程式碼元素是指一個專案(也可以是包或者型別),該專案被程式中被其他專案大量使用。 在程式碼中,不應該大量使用具體型別。這會在程式中帶來“痛苦區(Zones of Pains)”,當改變某個具體實現時,會潛在的影響程式的其他部分。而使用實現比抽象影響的範圍更大。 下圖中的主線(虛線)表示的是應該如何維持抽象和穩定性的平衡。穩定的元件應該位於左邊。如果檢查主線,可以看出,該線附近的元件抽象度都很高。如果抽象度非常低,則會位於“痛苦區”。  
只有工具專案位於“痛苦區”,但這並不是大問題。實際上,工具庫提供的是一些工具類,而不像介面提供功能。 7、Open API和外掛系統 可以用外掛擴充套件Intellij IDEA。“openapi”就是用來完成這個目標的jar。 openapi這個jar提供了許多介面,這些介面表示可以通過外掛擴充套件的特性。  
每個Intellij IDEA外掛含有一個或多個行為(Action)。下面這個CQLinq查詢結果中,顯示了程式碼中實現的數千個行為。  
研究已有的行為可以幫助開發者更容易開發自定義外掛。 8、使用快取提高效能 優化應用最常用的方式就是使用快取。Intellij IDEA使用兩個快取管理器:  
FindInProjectTask使用CacheManager介面來搜尋單詞。 下面列出了FindInProjectTask.getFilesForFastWordSearch方法呼叫的其他方法:  
9、使用的外部庫 Intellij IDEA使用了許多外部jar,下面列出了所有用到的外部jar:  
 
當使用了外部庫,最好檢查一下是否能方便的使用其他第三方庫而不對整個應用造成影響。在許多情況下都需要使用其他第三方庫。其他第三方庫會有以下特性:
  • 特性更多
  • 更強大
  • 更安全
現在來看看這些外部庫的耦合度是否很高。 Swing Swing實現了許多GUI元件,為Java應用添加了許多GUI功能和互動功能。Swing元件完全用Java語言實現。其可替換的外觀和體驗可以在不同的平臺上擁有相同的GUI外觀,也能在當前的系統上擁有本地的外觀(如Windows、Solaris或Linux)。 現在來看看所有直接使用Swing元件的型別:  
從下圖可以看出,藍色的型別都直接使用Swing元件。  
用其他GUI框架來替代Swing很困難。雖然Swing的爭議很大,但Intellij IDEA這樣優秀的GUI應用證明了Swing作為GUI庫也是很好的選擇。 Netty Netty是同步的時間驅動網路應用框架,用於快速開發易維護的高效能服務端、客戶端協議。 下面列出了所有實用該庫的型別:  
其中只有少數是直接使用了該庫,這樣如果想使用其他庫替換也非常容易。 ASM ASM是一個非常小的Java位元組碼操作框架。這個框架非常有名,許多工具都在使用。在JArchitect中也使用它來分析位元組碼。 下面列出了所有直接使用ASM的型別:  
如Netty一樣,對ASM的使用也僅限於幾個包中,這樣就可以方便的用其他庫替換。 除了Swing,其他庫都沒有在Intellij IDEA中緊耦合。 10、統計10.1、使用最多的型別 瞭解專案中那些型別使用的最多是一件非常有趣的事。這些型別必須在設計上、實現上都非常優秀,而且經過完善的測試。這些型別中的一丁點改動都會影響整個專案。 可以使用TypesUsingMe衡量標準來找到這些型別:  
然而,還有另一種有趣的衡量標準類搜尋常用型別:TypeRank。 TypeRank值是使用Google PageRank演算法在圖上計算型別的依賴關係。使用的位似中心(homothety of center)為0.15,所以平均TypeRank為1。 TypeRank值較高的型別應該仔細測試,因為這些型別中的bug會導致更大的問題。 下面是使用TypeRank衡量的所有常用型別:  
在這種衡量方式中,PsiElement取代了Project介面,成為最常用的型別。 10.2、使用最多的方法:  
10.3、過多呼叫其他方法的方法 瞭解某個方法呼叫了多少個其他方法也很有趣,如果一個方法過多呼叫其他方法可能會有設計問題。在某些情況下需要對這些方法進行重構,以提高可讀性和維護性。  
總結 Intellij IDEA的設計和實現都非常好,其中使用了許多設計模式,並提供了許多優秀的實踐經驗。探索其中的程式碼是一條實踐之路,從中可以學習如何設計和實現你的應用。這比只讀書和文件來提高設計技能要好得多。