第3章 模塊化

首先,我們想弄清在圍繞模塊化的架構(gòu)的討論中使用和經(jīng)常使用的一些通用術(shù)語,并提供在本書中使用的定義。

[關(guān)于軟件架構(gòu)]的詞語中有95%用于贊揚模塊化的好處,而關(guān)于如何實現(xiàn)模塊化的話很少。

格蘭福德·邁爾斯(Glenford J.Myers)(1978)

不同的平臺為代碼提供了不同的重用機(jī)制,但是所有平臺都支持以某種方式將相關(guān)代碼分組到模塊中。雖然這個概念在軟件體系結(jié)構(gòu)中是通用的,但事實證明它的定義很難。隨意在互聯(lián)網(wǎng)搜索會產(chǎn)生數(shù)十個定義,沒有一致性(也有一些矛盾)。從Myers的引文中可以看出,這不是一個新問題。但是,由于不存在公認(rèn)的定義,因此為了整本書的一致性,我們必須跳入爭端并提供我們自己的定義。

在選擇的開發(fā)平臺中了解模塊化及其多種形式對于架構(gòu)師至關(guān)重要。我們必須分析架構(gòu)的許多工具(例如指標(biāo),適度函數(shù)和可視化)都依賴于這些模塊化概念。模塊化是一種組織原則。如果架構(gòu)師在設(shè)計系統(tǒng)時沒有注意各個部分如何連接在一起,那么他們最終會創(chuàng)建一堆很難以維護(hù)的系統(tǒng)?;谖锢韺W(xué)的參考,軟件工程對復(fù)雜的系統(tǒng)進(jìn)行建模時,這些系統(tǒng)傾向于熵(或無序)。為了將能量在物理系統(tǒng)中得以保持秩序。軟件系統(tǒng)也是如此:架構(gòu)師必須不斷消耗精力以確保良好的結(jié)構(gòu)穩(wěn)健性,而不是讓它隨性發(fā)展。

保持良好的模塊化體現(xiàn)了我們對隱式架構(gòu)特征的定義:幾乎沒有項目有要求架構(gòu)師確保良好的模塊化區(qū)別和溝通的要求,而可持續(xù)的代碼庫則要求有序和一致性。

定義

通常將模塊定義為“每個可用于構(gòu)建更復(fù)雜結(jié)構(gòu)的一組標(biāo)準(zhǔn)化零件或獨立單元。”?我們使用模塊化來描述相關(guān)代碼的邏輯分組,該邏輯分組可以是面向?qū)ο笳Z言中的一組類,或者結(jié)構(gòu)化或功能性語言中的函數(shù)。大多數(shù)語言都提供了模塊化的機(jī)制(Java中的package,.NET 中的namespace)。開發(fā)人員通常使用模塊作為將相關(guān)代碼分組在一起的一種方式。例如,com.mycompany.customer 在Java包表示與客戶有關(guān)的內(nèi)容。

現(xiàn)代語言有各種各樣的打包機(jī)制,這使得開發(fā)人員很難在它們之間進(jìn)行自由的選擇。例如,在許多現(xiàn)代語言中,開發(fā)人員可以在函數(shù)/方法,類或包/命名空間中定義行為,每種行為都有不同的可見性和作用域規(guī)則。其他語言通過添加諸如meta object協(xié)議之類的編程構(gòu)造為開發(fā)人員提供更多擴(kuò)展機(jī)制,但也進(jìn)一步使這一問題變得復(fù)雜。

架構(gòu)師必須認(rèn)識到開發(fā)人員如何打包工件,因為它在架構(gòu)中具有重要意義。例如,如果幾個封裝緊密地耦合在一起,那么將其中一個組件重用于相關(guān)模塊將變得更加困難。

類中模塊化重用

早于面向?qū)ο笳Z言的開發(fā)人員可能會對為什么存在如此眾多的不同分離方案感到困惑。大部分系統(tǒng)兼容性不是與代碼的向后兼容性有關(guān),而是與開發(fā)人員如何考慮事物有關(guān)。1968年3月,埃德斯·迪克斯特拉(Edsger Dijkstra)ACM通訊中發(fā)表了一封公開信,題為“Go To Statement Considered

Harmful”。他否定了GOTO語句在編程語言中的普遍用法,當(dāng)時該用法允許在代碼內(nèi)進(jìn)行非線性跳躍,從而使推理和調(diào)試變得困難。

本文幫助引入了以Pascal和C為例的結(jié)構(gòu)化編程語言時代,這鼓勵人們對事物如何組合進(jìn)行更深入的思考。開發(fā)人員很快意識到,大多數(shù)語言都沒有很好的方法將邏輯上相似的東西組合在一起。因此,模塊化語言的短暫時代誕生了,例如Modula(Pascal的創(chuàng)建者Niklaus Wirth的下一種語言)和Ada。這些語言具有模塊的編程構(gòu)造,就像我們今天對包或名稱空間(但沒有類)的考慮一樣。

模塊化編程時代是短暫的。面向?qū)ο蟮恼Z言之所以流行,是因為它們提供了封裝和重用代碼的新方法。盡管如此,語言設(shè)計師還是意識到了模塊的效用,將它們保留為包,名稱空間等形式。語言中存在許多奇怪的兼容性功能,以支持這些不同的范例。例如,Java支持模塊化(通過使用靜態(tài)初始化程序的程序包和程序包級初始化),面向?qū)ο蠛凸δ芊妒?,每種編程風(fēng)格都有自己的作用域規(guī)則和怪癖。

對于架構(gòu)的討論,我們使用模塊化作為通用術(shù)語來表示相關(guān)的代碼分組:類,函數(shù)或任何其他分組。這并不意味著物理上的分離,而只是邏輯上的分離。差異有時也很重要。例如,從便捷的角度來看,將大量類集中在一起在單片應(yīng)用程序中可能是有意義的。但是,當(dāng)需要重組架構(gòu)時,松散分區(qū)所促進(jìn)的耦合將成為將整體分離的障礙。因此,將模塊化作為與特定平臺強(qiáng)制或隱含的物理分隔分開的概念進(jìn)行討論是有用的。

值得注意的是,命名空間的一般概念與.NET平臺中的技術(shù)實現(xiàn)是分開的。開發(fā)人員通常需要精確,完全合格的軟件資產(chǎn)名稱,以將不同的軟件資產(chǎn)(組件,類等)彼此分開。人們每天使用的最明顯的例子是互聯(lián)網(wǎng):綁定到IP地址的唯一的全局標(biāo)識符。大多數(shù)語言都有某種模塊化機(jī)制,可以兼作命名空間來組織事物:變量,函數(shù)或方法。有時,模塊結(jié)構(gòu)會在物理上反映出來。例如,Java要求其程序包結(jié)構(gòu)必須反映物理類文件的目錄結(jié)構(gòu)。

沒有命名沖突的語言:JAVA 1.0

Java的原始設(shè)計者在處理各種編程平臺中的名稱沖突和處理沖突方面具有豐富的經(jīng)驗。Java的原始設(shè)計使用了很多巧妙的技巧,以避免在兩個具有相同名稱的類之間產(chǎn)生歧義的可能性。例如,如果您的問題域包括目錄順序和安裝順序:兩者都命名為順序,但是具有不同的含義(和類)。Java的解決方案是創(chuàng)建package名稱空間機(jī)制,并要求物理目錄結(jié)構(gòu)僅與程序包名稱匹配。由于文件系統(tǒng)不允許相同的命名文件位于同一目錄中,因此它們利用操作系統(tǒng)的固有功能來避免產(chǎn)生歧義的可能性。因此,classpathJava中的原始版本僅包含目錄,從而避免了名稱沖突的可能性。

但是,正如語言設(shè)計人員所發(fā)現(xiàn)的那樣,強(qiáng)迫每個項目都具有完整的目錄結(jié)構(gòu)非常麻煩,尤其是隨著項目規(guī)模的擴(kuò)大。此外,構(gòu)建可重復(fù)使用的工件非常困難:必須將框架和庫“分解”到目錄結(jié)構(gòu)中。在Java的第二個主要版本(1.2,稱為Java 2)中,設(shè)計人員添加了該jar機(jī)制,允許存檔文件充當(dāng)類路徑上的目錄結(jié)構(gòu)。在接下來的十年中,Java開發(fā)人員一直努力將classpath目錄和JAR文件結(jié)合在一起,以實現(xiàn)正確的選擇。?當(dāng)然,最初的意圖已被破壞:現(xiàn)在,兩個JAR文件可以在類路徑上創(chuàng)建沖突的名稱,從而引發(fā)了許多調(diào)試類加載器的故事。

度量模塊

考慮到模塊化對架構(gòu)師的重要性,他們需要工具來理解它。?幸運的是,研究人員創(chuàng)建了各種與語言無關(guān)的指標(biāo),以幫助架構(gòu)師理解模塊化。我們專注于三個關(guān)鍵概念:內(nèi)聚性,耦合性共生性

內(nèi)聚

內(nèi)聚性是指一個模塊的各個部分應(yīng)該包含在同一個模塊中的程度。換言之,它是衡量各部分之間的關(guān)聯(lián)程度。理想情況下,內(nèi)聚模塊是所有部件都應(yīng)該打包在一起的模塊,因為將它們拆分成更小的部分需要通過模塊之間的調(diào)用將這些部件耦合在一起以獲得有用的結(jié)果。

嘗試分割內(nèi)聚模塊只會導(dǎo)致耦合性增加和可讀性下降。

拉里·康斯坦丁

計算機(jī)科學(xué)家定義了一系列內(nèi)聚性度量,從最佳到最壞列出如下:

功能內(nèi)聚

模塊的每個部分都相互關(guān)聯(lián),并且該模塊包含功能必需的所有內(nèi)容。

順序銜接

兩個模塊進(jìn)行交互,其中一個輸出數(shù)據(jù),而另一個成為輸入。

通信內(nèi)聚

兩個模塊構(gòu)成了一條通信鏈,其中每個模塊都基于信息進(jìn)行操作和/或有助于某些輸出。例如,將記錄添加到數(shù)據(jù)庫并基于該信息生成電子郵件。

程序內(nèi)聚

兩個模塊必須以特定順序執(zhí)行代碼。

時間內(nèi)聚

模塊基于時序依賴性而相關(guān)。例如,許多系統(tǒng)列出了一些看起來不相關(guān)的事物,必須在系統(tǒng)啟動時對其進(jìn)行初始化。這些不同的任務(wù)在時間上具有凝聚力。

邏輯內(nèi)聚

模塊內(nèi)的數(shù)據(jù)在邏輯上相關(guān),但在功能上無關(guān)。例如,考慮一個模塊,該模塊可以轉(zhuǎn)換文本,序列化對象或流中的信息。操作是相關(guān)的,但功能卻大不相同。實際上,每個Java項目都以StringUtils包的形式存在這種類型的內(nèi)聚性的常見示例:一組可在其上操作String但彼此不相關(guān)的靜態(tài)方法。

巧合內(nèi)聚

模塊中的元素除了在同一源文件中之外,不相關(guān)。這是凝聚力的最負(fù)面形式。

盡管列出了七個變體,但是內(nèi)聚性是不如耦合性精確的度量。通常,特定模塊的內(nèi)聚程度由特定架構(gòu)師決定。例如,考慮以下模塊定義:

Customer?Maintenance

add customer

update customer

get customer

notify customer

get customer orders

cancel customer orders

最后兩個條目應(yīng)該駐留在此模塊中還是開發(fā)人員應(yīng)該創(chuàng)建兩個單獨的模塊,例如:

Customer?Maintenance

add customer

update customer

get customer

notify customer

Order?Maintenance

get customer orders?

cancel customer orders

哪一個劃分是正確的結(jié)構(gòu)?與往常一樣,這取決于:

這是兩個唯一的Order Maintenance嗎?如果是這樣話,將這些操作重歸于Customer Maintenance是很有意義的。

Customer Maintenance是否會增長得更大,從而鼓勵開發(fā)人員尋找機(jī)會來提取行為?

是否Order Maintenance需要大量的Customer信息知識便于理解,以至于分離兩個模塊需要高度耦合才能使其發(fā)揮作用?這與拉里·康斯坦丁的名言有關(guān)。

這些問題代表了軟件架構(gòu)師工作核心的一種權(quán)衡分析。

出乎意料的是,考慮到內(nèi)聚性的主觀性,計算機(jī)科學(xué)家已經(jīng)開發(fā)出一種好的結(jié)構(gòu)度量來確定內(nèi)聚性(或更具體地說,缺乏內(nèi)聚性)。同名作者開發(fā)了一套著名的度量,稱為Chidamber和Kemerer面向?qū)ο蟮亩攘刻准?/a>,以度量面向?qū)ο蟮能浖到y(tǒng)的特定方面。該套件包括許多常見的代碼度量標(biāo)準(zhǔn),例如圈復(fù)雜度(請參閱“圈復(fù)雜度”)和“耦合”中討論的幾個重要的耦合度量。

Chidamber和Kemerer缺乏內(nèi)聚力(LCOM)度量標(biāo)準(zhǔn)測量模塊(通常是組件)的結(jié)構(gòu)內(nèi)聚方法。初始版本出現(xiàn)在公式3-1中。

公式3-1。LCOM版本1

對于不訪問特定共享字段的任何方法,P增大1,對于確實共享特定共享字段的方法,Q減小1。作者同情那些不理解這個公式的人。更糟糕的是,隨著時間的流逝,它變得越來越復(fù)雜。公式3-2中出現(xiàn)了1996年引入的第二個變化形式(因此命名為LCOM96B)。

公式3-2?LCOM 96b


由于下面的書面說明更加清楚,因此我們無需理方程3-2中的變量和運算符。事實上,LCOM度量公開類之間的偶發(fā)耦合。這是LCOM的更好定義:

LCOM

未通過共享字段共享的方法集的總和。

考慮一個具有私有字段a和b的類。許多方法只能訪問a,而許多其他方法只能訪問b。未通過共享字段(和)共享的方法的總和很高;因此,該課程的LCOM評分較高,表明該方法缺乏內(nèi)聚的情況下評分較高??紤]圖3-1所示的三個類。

3-1。LCOM度量的圖示,其中字段是八邊形,方法是正方形

圖3-1中,字段顯示為單個字母,方法顯示為塊。在中Class X,LCOM得分較低,表明結(jié)構(gòu)內(nèi)聚力良好。Class Y但是,缺乏內(nèi)聚力;每個字段/方法對都Class Y可以出現(xiàn)在自己的類中,而不會影響其行為。Class Z顯示了混合的內(nèi)聚力,開發(fā)人員可以將最后的字段/方法組合重構(gòu)為自己的類。

LCOM指標(biāo)對于正在分析代碼庫以從一種架構(gòu)樣式轉(zhuǎn)換為另一種架構(gòu)樣式的架構(gòu)師很有用。移動架構(gòu)是共享實用程序類時的常見頭痛之一。使用LCOM度量標(biāo)準(zhǔn)可以幫助架構(gòu)師找到偶然耦合的類,并且從一開始就不應(yīng)該是單個類。

許多軟件指標(biāo)存在嚴(yán)重缺陷,LCOM也無法幸免。所有這些度量可以發(fā)現(xiàn)的是結(jié)構(gòu)上缺乏凝聚力。它無法從邏輯上確定特定部分是否組合在一起。這反映回來的軟件架構(gòu)的我們的第二定律:為什么比如何做更重要。

耦合

幸運的是,我們有更好的工具來分析代碼庫中的耦合,部分基于圖論:由于方法調(diào)用和返回是以調(diào)用圖的形式進(jìn)行的,基于數(shù)學(xué)的分析成為可能。1979年,愛德華·尤登(Edward Yourdon)和拉里·康斯坦?。↙arry

Constantine)出版了《結(jié)構(gòu)化設(shè)計:計算機(jī)程序和系統(tǒng)設(shè)計學(xué)科的基礎(chǔ)》(Prentice-Hall),定義了許多核心概念,包括輸入輸出的度量標(biāo)準(zhǔn)。輸入耦合度量到代碼工件(組件,類,函數(shù)等)的輸入連接數(shù)。輸出耦合測與其他代碼工件的連接。實際上,幾乎所有平臺工具都可以使架構(gòu)師分析代碼的耦合特性,以幫助重組,遷移或理解代碼庫。

為什么對耦合度量使用如此相似的名稱?

為什么在架構(gòu)世界中,代表相反概念的兩個關(guān)鍵指標(biāo)實際上被命名為同一事物,只是在聽起來最相似的元音上有所不同?這些術(shù)語源自Yourdon和Constantine的結(jié)構(gòu)化設(shè)計。他們從數(shù)學(xué)中借用了概念,創(chuàng)造了現(xiàn)在常見的輸入和輸出耦合術(shù)語,這些術(shù)語應(yīng)稱為輸入和輸出耦合。然而,由于最初的傾向于數(shù)學(xué)對稱而不是清晰,開發(fā)者想出了幾個助記符來幫助他們:在英語字母表中,a出現(xiàn)在e之前,對應(yīng)于輸入在輸出之前,或者觀察到輸出中的字母eexit中的首字母相匹配,對應(yīng)于輸出連接。

抽象性,不穩(wěn)定性以及與主序列的距離

雖然組件耦合的原始值對架構(gòu)師有價值,但其他幾個衍生指標(biāo)也可以進(jìn)行更深入的評估。這些度量標(biāo)準(zhǔn)是由Robert Martin為一本C ++書籍創(chuàng)建的,但是廣泛適用于其他面向?qū)ο蟮恼Z言。

抽象性是抽象工件(抽象類,接口等)與具體工件(實現(xiàn))的比率。它代表了抽象性與實現(xiàn)性的度量。例如,考慮一個沒有抽象的代碼庫,只是一個巨大的單一代碼功能(如在單個main()方法中)。另一面是具有太多抽象的代碼庫,這使開發(fā)人員難以理解事物之間的連接方式(例如,開發(fā)人員需要一段時間才能弄清楚如何處理AbstractSingletonProxyFactoryBean)。

抽象性公式出現(xiàn)在公式3-3中。

公式3-3?抽象性

在等式中

用模塊表示抽象元素(接口或抽象類),并且

模塊表示具體元素(非抽象類)。此度量標(biāo)準(zhǔn)尋找相同的條件??梢暬酥笜?biāo)的最簡單方法:考慮一個具有5,000行代碼的應(yīng)用程序,并且全部采用一種main()方法。抽象分子為1,而分母為5,000,產(chǎn)生的抽象度幾乎為0。因此,此度量標(biāo)準(zhǔn)度量代碼中抽象率。

架構(gòu)師通過計算抽象工件總和與具體工件總和之比來計算抽象度。

另一個導(dǎo)出的度量標(biāo)準(zhǔn)(不穩(wěn)定性)定義為輸出耦合與(輸出和輸入耦合總和)之比,如公式3-4所示。

公式3-4。不穩(wěn)定性

在等式中

?表示輸出耦合,并且

表示輸入耦合。

不穩(wěn)定性度量決定代碼庫的波動。由于高度耦合,表現(xiàn)出高度不穩(wěn)定性的代碼庫在更ycbgq改時更容易破壞。例如,如果一個類調(diào)用許多其他類來委派工作,則當(dāng)一個或多個被調(diào)用方法發(fā)生更改時,被調(diào)用的類很容易發(fā)生損壞。

主序列的距離

架構(gòu)師為架構(gòu)提供的少數(shù)整體指標(biāo)之一是與主序列的距離,即基于不穩(wěn)定性抽象性的衍生指標(biāo),如公式3-5所示。

公式3-5。距主要序列的距離

D=|A+I?1|

在等式中,A=?抽象,I =?不穩(wěn)定性。

請注意,抽象性不穩(wěn)定性都是比率,這意味著它們的結(jié)果將始終落在0和1之間。因此,在繪制關(guān)系圖時,我們看到的是圖3-2中的。


3-2主要序列定義了抽象性與不穩(wěn)定性之間的理想關(guān)系

所述主序列距離度量不可能是抽象性和不穩(wěn)定性之間的理想關(guān)系;?接近理想線的類將這兩個相互關(guān)注的問題很好地融合在一起。例如,繪制特定的類可以使開發(fā)人員計算距主序列度量的距離,如圖3-3所示


3-3。特定類與主序列的歸一化距離

圖3-3中,開發(fā)人員繪制候選類的圖,然后測量距理想線的距離。距離線越近,類的平衡就越好。過于右上角的類進(jìn)入了架構(gòu)師所謂的無用區(qū)域:太抽象的代碼變得難以使用。相反,落入左下角的代碼將進(jìn)入痛苦區(qū)域:實現(xiàn)過多而抽象性不足的代碼變得脆弱且難以維護(hù),如圖3-4所示

3-4無用和痛苦的區(qū)域

許多平臺中都存在提供這些措施的工具,這些工具可以幫助架構(gòu)師在分析代碼庫時由于不熟悉,遷移或技術(shù)債務(wù)評估而導(dǎo)致。

指標(biāo)的局限性

盡管行業(yè)中有一些代碼級指標(biāo)可提供對代碼庫的寶貴見解,但與其他工程學(xué)科的分析工具相比,我們的工具非常遲鈍。即使直接從代碼結(jié)構(gòu)中得出的指標(biāo)也需要解釋。例如,循環(huán)復(fù)雜度(請參閱“循環(huán)復(fù)雜度”)可以測量代碼庫中的復(fù)雜度,但無法與基本復(fù)雜度(因為底層問題很復(fù)雜)或意外復(fù)雜度區(qū)分開來。(代碼比實際的要復(fù)雜得多)。幾乎所有代碼級度量都需要解釋,但是為關(guān)鍵度量(例如圈復(fù)雜度)建立基線仍然很有用,以便架構(gòu)師可以評估它們所顯示的類型。我們將在“治理和適應(yīng)度函數(shù)”中討論設(shè)置此類測試。

請注意,Edward Yourdon和Larry Constantine先前提到的書(《結(jié)構(gòu)化設(shè)計:計算機(jī)程序和系統(tǒng)設(shè)計學(xué)科的基礎(chǔ)》)早于面向?qū)ο笳Z言的流行,而側(cè)重于結(jié)構(gòu)化的編程構(gòu)造,例如函數(shù)(不是方法)。它還定義了其他類型的耦合,在此不做介紹,因為它們已被共生性取代了。

共生性

1996年,Meilir Page-Jones出版了《每個程序員應(yīng)該了解的有關(guān)面向?qū)ο蟮脑O(shè)計》(Dorset House),完??善了輸入和輸出的度量標(biāo)準(zhǔn),并用他稱為共生性的概念將其轉(zhuǎn)換為面向?qū)ο蟮恼Z言。這是他對術(shù)語的定義:

如果一個組件的變更需要修改另一個組件以保持系統(tǒng)的整體正確性,則兩個組件是相鄰的。

梅里爾·佩奇·瓊斯

他開發(fā)了兩種類型的共生性:靜態(tài)動態(tài)

靜態(tài)共生性

靜態(tài)共生性是指源代碼級的耦合(與執(zhí)行時耦合相對,在“動態(tài)共生性”中進(jìn)行了介紹);它是對結(jié)構(gòu)化設(shè)計定義的輸入和輸出耦合的改進(jìn)。換言之,架構(gòu)師將以下類型的靜態(tài)連接視為某種事物的耦合程度,無論是傳入的還是有效的:

名稱共生性(CoN

多個組件必須在實體名稱上達(dá)成一致。

方法名表示代碼基耦合的最常見方式,也是最理想的方法,尤其是在現(xiàn)代重構(gòu)工具的情況下,這些工具使得系統(tǒng)范圍內(nèi)的名稱更改變得微不足道。

類型共生性(CoT

多個組件必須在實體類型上達(dá)成共識。

這種共生性是指許多靜態(tài)類型語言中的通用功能,用于將變量和參數(shù)限制為特定類型。但是,此功能并不是純粹的語言功能-一些動態(tài)類型化的語言提供選擇性鍵入,尤其是ClojureClojure Spec。

含義共生性(CoM)或常識共生性(CoC

多個組件必須在特定值的含義上達(dá)成共識。

在代碼庫中這種類型的共生性最常見的情況是硬編碼數(shù)字而不是常量。例如,在某些語言中,通??紤]在某處定義int TRUE = 1; int FALSE = 0。想象一下,如果有人放棄了這些價值觀,就會出現(xiàn)問題。

職位共生性(CoP

多個實體必須就價值順序達(dá)成一致。

這是方法和函數(shù)調(diào)用的參數(shù)值存在的問題,即使在具有靜態(tài)類型的語言中也是如此。例如,如果開發(fā)人員創(chuàng)建一個方法void updateSeat(String name, String seatLocation)并使用value調(diào)用它,則updateSeat("14D", "Ford, N")即使類型正確,語義也不正確。

算法共生性(CoA

多個組件必須在特定算法上達(dá)成共識。

當(dāng)開發(fā)人員定義必須在服務(wù)器和客戶端上運行并產(chǎn)生相同結(jié)果以認(rèn)證用戶的安全哈希算法時,就會發(fā)生這種類型的共生性。顯然,這代表了一種較高的耦合形式-如果任何一種算法更改了任何細(xì)節(jié),握手將不再起作用。

動態(tài)共生性

Page-Jones定義的另一種共生性是動態(tài)共生性,它在運行時分析調(diào)用。以下是對動態(tài)共生性的不同類型的描述:

執(zhí)行共生性(CoE

多個組件的執(zhí)行順序很重要。

考慮以下代碼:

email = new Email();

email.setRecipient("foo@example.com");

email.setSender("me@me.com");

email.send();

email.setSubject("whoops");

由于某些屬性必須按順序設(shè)置,因此無法正常工作。

時機(jī)共生性(CoT

執(zhí)行多個組件的時間很重要。

這種類型的共生性的常見情況是由兩個線程同時執(zhí)行導(dǎo)致的競爭狀態(tài),從而影響聯(lián)合操作的結(jié)果。

價值共生性(CoV

當(dāng)多個值相互關(guān)聯(lián)并且必須一起更改時發(fā)生。

考慮開發(fā)人員將矩形定義為代表角的四個點的情況。為了保持?jǐn)?shù)據(jù)結(jié)構(gòu)的完整性,開發(fā)人員無法在不考慮對其他點的影響的情況下隨機(jī)更改其中一個點。

更常見和有問題的情況涉及事務(wù),尤其是在分布式系統(tǒng)中。當(dāng)架構(gòu)師設(shè)計具有單獨數(shù)據(jù)庫的系統(tǒng),但需要在所有數(shù)據(jù)庫中更新單個值時,所有值必須一起更改或根本不更改。

身份共生性(CoI

當(dāng)多個值相互關(guān)聯(lián)并且必須一起更改時發(fā)生。

這種類型的連接的常見示例涉及兩個獨立的組件,這些組件必須共享和更新公共數(shù)據(jù)結(jié)構(gòu),例如分布式隊列。

架構(gòu)師很難確定動態(tài)連通性,因為我們?nèi)狈δ軌蛳穹治稣{(diào)用圖一樣有效地分析運行時調(diào)用的工具。

共生性屬性

共生性是面向架構(gòu)師和開發(fā)人員的分析工具,并且邊續(xù)性的某些屬性可幫助開發(fā)人員明智地使用它。以下是每個這些共生性屬性的描述:

強(qiáng)度

架構(gòu)師通過開發(fā)人員重構(gòu)這種耦合的容易程度來確定連接的強(qiáng)度;不同類型的連接顯然更可取,如圖3-5所示。架構(gòu)師和開發(fā)人員可以通過重構(gòu)以獲得更好的連接類型來改善代碼庫的耦合特性。

架構(gòu)師應(yīng)該更喜歡靜態(tài)共生性,而不是動態(tài),因為開發(fā)人員可以通過簡單的源代碼分析來確定靜態(tài)共生性,而現(xiàn)代工具則使靜態(tài)共生性變得微不足道。例如,考慮含義的共生性的情況,開發(fā)人員可以通過創(chuàng)建命名常量而不是魔術(shù)值來重構(gòu)名稱共生性,從而改善這種情況。

3-5。實力connascence提供了良好的重構(gòu)指南

區(qū)域性

共生性的位置可衡量模塊在代碼庫中彼此之間的距離。與在不同模塊(在獨立模塊或代碼庫中)分離的代碼相比,近鄰代碼(在同一模塊中)通常具有越來越多的形式。換句話說,如果相距很近,則表示耦合不良的連通形式會很好。例如,如果同一組件中的兩個類具有意義的共生性,則與兩個組件具有相同形式的共生性相比,它對代碼庫的破壞較小。

開發(fā)人員必須同時考慮強(qiáng)度和區(qū)域性。在同一模塊中發(fā)現(xiàn)的更強(qiáng)形式的共生性表示的代碼嗅覺比散布的同一個共生性少。

共生性的程度與影響的大小有關(guān)—它影響幾個還是多個班級?較小的共生性損壞代碼基礎(chǔ)較少。換句話說,如果您只有幾個模塊,那么具有高動態(tài)共生性并不可怕。但是,代碼庫趨于增長,相應(yīng)地,一個小問題也變得更大。

Page-Jones提供了三種使用共生性改善系統(tǒng)模塊化的準(zhǔn)則?:

通過將系統(tǒng)分解為封裝的元素來最大程度地減少總體占用

最小化跨越封裝邊界的所有剩余內(nèi)容

最大化封裝邊界內(nèi)的共生性

富有傳奇色彩的軟件架構(gòu)創(chuàng)新者Jim Weirich重新流行了共生性的概念,并提供了兩條很好的建議:

等級規(guī)則:將強(qiáng)力的自閉癥轉(zhuǎn)化為弱勢的自閉癥

局部性規(guī)則:隨著軟件元素之間的距離增加,請使用較弱的形式

統(tǒng)一耦合和共生性度量

到目前為止,我們已經(jīng)討論了耦合和共生性,來自不同時代和針對不同目標(biāo)的措施。但是,從架構(gòu)師的角度來看,這兩個視圖是重疊的。Page-Jones識別為靜態(tài)共生性的是輸入或輸出耦合的程度。結(jié)構(gòu)化編程只關(guān)心輸入或輸出,而共生性關(guān)心的是事物如何耦合在一起。

為了幫助可視化概念上的重疊,請考慮圖3-6。結(jié)構(gòu)化的編程耦合概念顯示在左側(cè),而共生性特征顯示在右側(cè)。共生性被結(jié)構(gòu)化編程稱為數(shù)據(jù)耦合(方法調(diào)用),它為應(yīng)該如何表現(xiàn)這種耦合提供了建議。結(jié)構(gòu)化編程并沒有真正解決動態(tài)融合所涉及的領(lǐng)域。我們很快將這個概念封裝在“架構(gòu)量子和粒度”中


3-6。統(tǒng)一耦合和連續(xù)

1990年代出生的問題

架構(gòu)師在將這些有用的指標(biāo)用于系統(tǒng)分析和設(shè)計時存在一些問題。首先,這些措施著眼于低級別代碼的細(xì)節(jié),側(cè)重于代碼質(zhì)量和健壯性,而不是架構(gòu)。架構(gòu)師傾向于更關(guān)心如何模塊耦合,而不是耦合。例如,架構(gòu)師關(guān)心同步通信與異步通信,而不關(guān)心實現(xiàn)方式。

共生性的第二個問題在于,它并沒有真正解決許多現(xiàn)代架構(gòu)師必須做出的基本決定,即微服務(wù)等分布式架構(gòu)中的同步或異步通信?回到軟件架構(gòu)第一定律,一切架構(gòu)決策都是權(quán)衡。在第7章中討論了建筑特征的范圍之后,我們將介紹思考現(xiàn)代現(xiàn)代性的新方法。

從模塊到組件

我們將術(shù)語模塊始終用作捆綁相關(guān)代碼的通用名稱。但是,大多數(shù)平臺都支持某種形式的組件,這是軟件架構(gòu)師的關(guān)鍵組成部分之一。自計算機(jī)科學(xué)成立以來,就已經(jīng)存在邏輯分離或物理分離的概念和相應(yīng)的分析方法。然而,盡管所有關(guān)于組件和分離的寫作和思考,開發(fā)人員和架構(gòu)師仍在努力取得良好的成果。

我們將在第8章中討論從問題域派生組件,但首先必須討論軟件架構(gòu)的另一個基本方面:架構(gòu)特征及其范圍。

原文 :http://m.itdecent.cn/p/5b8121fbf629


聲明:本資料僅供學(xué)習(xí)交流嚴(yán)禁使用于任何商業(yè)用途!請購買正版圖書:https://www.oreilly.com/library/view/fundamentals-of-software/9781492043447/cover.html

資料整理和翻譯:楊傳池Chris? IT老兵,人生三大愛好(愛好喝茶,喝酒和喜歡做夢)

10+年的軟件研發(fā)和項目管理經(jīng)驗;

7+年大型房產(chǎn)信息化、數(shù)字化咨詢經(jīng)驗;

50+人以上研發(fā)團(tuán)隊管理,擅長團(tuán)隊管理和人才梯隊建設(shè);

熟悉研發(fā)管理、工程構(gòu)建、體系建設(shè)、DevOps和領(lǐng)域建模,掌握IT研發(fā)價值鏈和工具鏈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容