【編者按】高內(nèi)聚是軟件架構(gòu)中經(jīng)常被忽視的基石。本文將介紹高內(nèi)聚的含義,重要性和實(shí)現(xiàn)高內(nèi)聚的方法,如遵循單一職責(zé)原則、分解復(fù)雜的類(lèi)、保持內(nèi)聚的操作集和避免"上帝"對(duì)象等。
原文鏈接: /the-principle-of-high-cohesion-a-pillar-of-reliable-software-design/
(資料圖片僅供參考)
未經(jīng)允許,禁止轉(zhuǎn)載!
圖片來(lái)源: Ross Sneddon / Unsplash
今天, 我們要探討軟件設(shè)計(jì)中一個(gè)核心原則: 高內(nèi)聚 。這是軟件架構(gòu)中一個(gè)常被忽視的基石,它能決定你的代碼的可靠性和效率。
在我們學(xué)習(xí)高內(nèi)聚的具體內(nèi)容之前,首先必須理解軟件開(kāi)發(fā)中內(nèi)聚性的含義。簡(jiǎn)單來(lái)說(shuō),內(nèi)聚性是指系統(tǒng)中一個(gè)模塊、類(lèi)或組件中的職責(zé)之間的緊密程度。當(dāng)我們談?wù)摗案邇?nèi)聚”時(shí),是指一個(gè)模塊或類(lèi)擁有單一、明確定義的角色或職責(zé)的場(chǎng)景。
高內(nèi)聚不僅是一個(gè)理論概念,它在軟件設(shè)計(jì)中具有明確的、實(shí)際的好處:
簡(jiǎn)單性和可理解性 : 當(dāng)每個(gè)模塊只有一個(gè)明確定義的功能時(shí),它變得更簡(jiǎn)單直觀。 這種簡(jiǎn)單性延伸到任何處理代碼的人,使其他開(kāi)發(fā)者更容易維護(hù)和增強(qiáng)。
更易維護(hù)和修改 : 高內(nèi)聚通常導(dǎo)致較少的依賴(lài)性。 較少的依賴(lài)性意味著系統(tǒng)中一個(gè)部分的變化不太可能影響其他部分,減少了錯(cuò)誤的可能性,并簡(jiǎn)化了修改。
增加重用性 : 當(dāng)一個(gè)模塊被設(shè)計(jì)成只有單一職責(zé)時(shí),它就成為一個(gè)高度可重用的組件。 你可以在應(yīng)用程序的不同部分重用這個(gè)組件,甚至跨不同的項(xiàng)目。
更好的測(cè)試和調(diào)試 : 由于每個(gè)模塊可以被隔離地進(jìn)行測(cè)試,測(cè)試變得更簡(jiǎn)單。 任何發(fā)現(xiàn)的錯(cuò)誤也更容易跟蹤和修復(fù),因?yàn)樗鼈兛赡芫窒抻诖a庫(kù)中一個(gè)特定的、明確定義的區(qū)域。
實(shí)現(xiàn)高內(nèi)聚并不總是簡(jiǎn)單明了的。它需要仔細(xì)的設(shè)計(jì)決策。這里提供一些參考原則,幫助你的代碼庫(kù)保持高度內(nèi)聚:
每個(gè)類(lèi)或模塊應(yīng)該只有一個(gè)修改的原因。單一職責(zé)原則是 SOLID 設(shè)計(jì)原則之一(SOLID 是單一職責(zé)原則、開(kāi)閉原則、里式替換原則、接口隔離原則和依賴(lài)反轉(zhuǎn)原則的總稱(chēng)),它規(guī)定一個(gè)類(lèi)應(yīng)該只有一個(gè)職責(zé)。這可以作為維護(hù)高內(nèi)聚的指導(dǎo)方針。
例如,假設(shè)我們?cè)诮灰讘?yīng)用程序中有一個(gè)名為 TradeManager 的類(lèi),它目前負(fù)責(zé)下單和記錄交易活動(dòng)。這個(gè)設(shè)計(jì)違反了單一職責(zé)原則(SRP),因?yàn)樵擃?lèi)具有多個(gè)變更的原因。
它可能如下所示:
type TradeManager struct {//...}func (t *TradeManager) placeTrade(stockSymbol string, quantity int, tradeType string) {// 下單邏輯//...}func (t *TradeManager) logTradeActivity(tradeActivity string) {// 記錄交易活動(dòng)邏輯//...}
為了遵循 SRP 和實(shí)現(xiàn)高內(nèi)聚,我們應(yīng)該將這些職責(zé)分離到兩個(gè)不同的類(lèi)中。一個(gè)類(lèi)可以處理下單,另一個(gè)類(lèi)可以處理記錄交易活動(dòng)。
重構(gòu)后的代碼如下所示:
type TradeManager struct {//...}func (t *TradeManager) placeTrade(stockSymbol string, quantity int, tradeType string) {// 下單邏輯//...}type TradeActivityLogger struct {//...}func (l *TradeActivityLogger) logTradeActivity(tradeActivity string) {// 記錄交易活動(dòng)邏輯//...}
在重構(gòu)后的版本中,TradeManager 和 TradeActivityLogger 各自只有一個(gè)職責(zé),使代碼更具內(nèi)聚性,也更易于維護(hù)。
如果發(fā)現(xiàn)一個(gè)類(lèi)做了太多事情,可以將其分解成多個(gè)更易管理的類(lèi),每個(gè)類(lèi)只負(fù)責(zé)一個(gè)職責(zé)。這種分解將提高軟件的整體內(nèi)聚性。
我們來(lái)看一個(gè) OrderManager 類(lèi)的示例,它管理訂單的所有方面,包括創(chuàng)建、驗(yàn)證、執(zhí)行、取消、列出和獲取訂單。
這個(gè)類(lèi)顯然職責(zé)過(guò)多:
type OrderManager struct {//...}func (o *OrderManager) createOrder(stockSymbol string, quantity int, orderType string) {// 創(chuàng)建新訂單邏輯//...}func (o *OrderManager) validateOrder(order Order) bool {// 驗(yàn)證訂單邏輯//...}func (o *OrderManager) executeOrder(order Order) {// 執(zhí)行訂單邏輯//...}func (o *OrderManager) cancelOrder(order Order) {// 取消訂單邏輯//...}func (o *OrderManager) listOrders() []Order {// 列出所有訂單邏輯//...}func (o *OrderManager) getOrder(orderId string) Order {// 獲取特定訂單邏輯//...}
這個(gè)類(lèi)的職責(zé)過(guò)多,違反了單一職責(zé)原則。我們可以將職責(zé)分離到 OrderManager 和 OrderRepository 兩個(gè)類(lèi)中。
OrderManager 類(lèi)將負(fù)責(zé)與訂單生命周期直接相關(guān)的操作,如創(chuàng)建、驗(yàn)證、執(zhí)行和取消訂單。 OrderRepository 將處理面向數(shù)據(jù)的操作,如列出和獲取特定訂單。
type OrderManager struct {//...}func (o *OrderManager) createOrder(stockSymbol string, quantity int, orderType string) {// 創(chuàng)建新訂單邏輯//...}func (o *OrderManager) validateOrder(order Order) bool {// 驗(yàn)證訂單邏輯//...}func (o *OrderManager) executeOrder(order Order) {// 執(zhí)行訂單邏輯//...}func (o *OrderManager) cancelOrder(order Order) {// 取消訂單邏輯//...}type OrderRepository struct {//...}func (r *OrderRepository) listOrders() []Order {// 列出所有訂單邏輯//...}func (r *OrderRepository) getOrder(orderId string) Order {// 獲取特定訂單邏輯//...}
通過(guò)將職責(zé)分離到 OrderManager 和 OrderRepository 類(lèi)中,設(shè)計(jì)現(xiàn)在更符合單一職責(zé)原則,提高了代碼的內(nèi)聚性、可維護(hù)性和可讀性。每個(gè)類(lèi)可以獨(dú)立開(kāi)發(fā)、修改和測(cè)試,減少一個(gè)類(lèi)的變更會(huì)不經(jīng)意地影響另一個(gè)類(lèi)的可能性。
確保模塊或類(lèi)中的操作形成一個(gè)內(nèi)聚的集合。如果有不太適合的操作,請(qǐng)考慮將其移動(dòng)到另一個(gè)更合適的模塊或創(chuàng)建一個(gè)新的模塊。
保持操作集的內(nèi)聚性意味著給定模塊或類(lèi)中的操作是緊密相關(guān)的,并有助于實(shí)現(xiàn)單一職責(zé)。以下是一個(gè)交易系統(tǒng)中 StockTrade 類(lèi)的示例:
type StockTrade struct {stockSymbol stringquantity inttradeType string}func (s *StockTrade) setStockSymbol(stockSymbol string) {= stockSymbol}func (s *StockTrade) setQuantity(quantity int) {= quantity}func (s *StockTrade) setTradeType(tradeType string) {= tradeType}func (s *StockTrade) getStockSymbol() string {return}func (s *StockTrade) getQuantity() int {return}func (s *StockTrade) getTradeType() string {return}
在上面的例子中, StockTrade 類(lèi)維護(hù)了一個(gè)內(nèi)聚的操作集。所有的 getter 和 setter 方法都與股票交易的屬性相關(guān)。
例如,如果我們?cè)谶@個(gè)類(lèi)中添加執(zhí)行交易或記錄交易執(zhí)行的方法,那將違反高內(nèi)聚原則,因?yàn)閳?zhí)行和記錄是不同的職責(zé),不屬于 StockTrade 類(lèi)。相反,執(zhí)行和記錄應(yīng)該委托給專(zhuān)門(mén)設(shè)計(jì)用來(lái)處理這些其他目的的不其他類(lèi)。
“上帝”對(duì)象是知道太多或做太多事情的對(duì)象。這些是低內(nèi)聚的對(duì)象,維護(hù)起來(lái)很困難。將這樣的對(duì)象分解成更小、高度內(nèi)聚的組件可以提高可理解性和可維護(hù)性。
我們來(lái)看一個(gè)交易應(yīng)用程序中“上帝”接口的例子。我們將這個(gè)接口定義為 TradingSystem。它試圖做與交易相關(guān)的所有事情,從管理股票、交易、訂單到用戶(hù)帳戶(hù)等。
type TradingSystem interface {addStock(stock Stock)removeStock(stock Stock)updateStock(stock Stock)listStocks() []StockgetStock(id string) StockplaceTrade(trade Trade)cancelTrade(trade Trade)listTrades() []TradegetTrade(id string) TradecreateOrder(order Order)validateOrder(order Order)executeOrder(order Order)cancelOrder(order Order)listOrders() []OrdergetOrder(id string) OrdercreateUser(user User)deleteUser(user User)updateUser(user User)listUsers() []UsergetUser(id string) User}
因?yàn)樗淮卧噲D做太多事情,所以該接口需要進(jìn)行拆分。它不僅知道添加/刪除/更新/列出股票,下單/取消/列出/獲取交易這樣的交易活動(dòng),還知道創(chuàng)建/刪除/更新/列出/獲取用戶(hù)這樣的用戶(hù)管理活動(dòng)。
這個(gè)接口可以分解成更具內(nèi)聚性和可管理性的接口,每個(gè)接口處理一個(gè)職責(zé),比如 StockManager 、 TradeManager 、 OrderManager 和 UserManager 。這樣,每個(gè)接口及其實(shí)現(xiàn)類(lèi)都更易于理解、維護(hù)和測(cè)試。
高內(nèi)聚是提高軟件健壯性和可維護(hù)性的經(jīng)典指導(dǎo)原則。它通過(guò)提高代碼的整潔性、可維護(hù)性、可重用性和可測(cè)試性,幫助構(gòu)建高可靠和高健壯的軟件。如果我們能夠多花一些時(shí)間確保模塊高度內(nèi)聚,不僅可以提高你代碼庫(kù)的健康度,而且能在未來(lái)提高軟件可擴(kuò)展性,確保它能夠被其他人輕松理解、測(cè)試和增強(qiáng)。
你還知道哪些提高代碼內(nèi)聚性的原則和技巧,歡迎在評(píng)論區(qū)留言分享。
標(biāo)簽: