:notebook: 本文已歸檔到:「blog」java
翻譯自:https://sourcemaking.com/refactoring/smells/couplersgit
耦合(Couplers)這組壞味道意味着:不一樣類之間過分耦合。程序員
不完美的庫類(Incomplete Library Class)github
當一個類庫已經不能知足實際須要時,你就不得不改變這個庫(若是這個庫是隻讀的,那就沒轍了)。編程
許多編程技術都創建在庫類的基礎上。庫類的做者沒用未卜先知的能力,不能所以責怪他們。麻煩的是庫每每構造的不夠好,並且每每不可能讓咱們修改其中的類以知足咱們的須要。設計模式
引入外加函數(Introduce Foreign Method)
;引入本地擴展(Introduce Local Extension)
。問題app
你須要爲提供服務的類增長一個函數,但你沒法修改這個類。ide
class Report {
//...
void sendReport() {
Date nextDay = new Date(previousEnd.getYear(),
previousEnd.getMonth(), previousEnd.getDate() + 1);
//...
}
}
複製代碼
解決函數
在客戶類中創建一個函數,並一個第一個參數形式傳入一個服務類實例。post
class Report {
//...
void sendReport() {
Date newStart = nextDay(previousEnd);
//...
}
private static Date nextDay(Date arg) {
return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}
}
複製代碼
問題
你須要爲服務類提供一些額外函數,但你沒法修改這個類。
解決
創建一個新類,使它包含這些額外函數,讓這個擴展品成爲源類的子類或包裝類。
中間人(Middle Man)
若是一個類的做用僅僅是指向另外一個類的委託,爲何要存在呢?
對象的基本特徵之一就是封裝:對外部世界隱藏其內部細節。封裝每每伴隨委託。可是人們可能過分運用委託。好比,你也許會看到一個類的大部分有用工做都委託給了其餘類,類自己成了一個空殼,除了委託以外不作任何事情。
應該運用 移除中間人(Remove Middle Man)
,直接和真正負責的對象打交道。
若是是如下狀況,不要刪除已建立的中間人:
問題
某個類作了過多的簡單委託動做。
解決
讓客戶直接調用委託類。
依戀情結(Feature Envy)
一個函數訪問其它對象的數據比訪問本身的數據更多。
這種氣味可能發生在字段移動到數據類以後。若是是這種狀況,你可能想將數據類的操做移動到這個類中。
As a basic rule, if things change at the same time, you should keep them in the same place. Usually data and functions that use this data are changed together (although exceptions are possible).
有一個基本原則:同時會發生改變的事情應該被放在同一個地方。一般,數據和使用這些數據的函數是一塊兒改變的。
搬移函數(Move Method)
。提煉函數(Extract Method)
將這部分代碼移到獨立的函數中。提煉函數(Extract Method)
將方法拆分爲幾個部分,能夠放置在不一樣類中的不一樣位置。問題
你的程序中,有個函數與其所駐類以外的另外一個類進行更多交流:調用後者,或被後者調用。
解決
在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是舊函數徹底移除。
問題
你有一段代碼能夠組織在一塊兒。
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
複製代碼
解決
移動這段代碼到一個新的函數中,使用函數的調用來替代老代碼。
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
複製代碼
狎暱關係(Inappropriate Intimacy)
一個類大量使用另外一個類的內部字段和方法。
類和類之間應該儘可能少的感知彼此(減小耦合)。這樣的類更容易維護和複用。
搬移函數(Move Method)
和 搬移字段(Move Field)
來讓類之間斬斷羈絆。你也能夠看看是否能運用 將雙向關聯改成單向關聯(Change Bidirectional Association to Unidirectional)
讓其中一個類對另外一個說分手。
若是這兩個類實在是情比金堅,難分難捨,能夠運用 提煉類(Extract Class)
把兩者共同點提煉到一個新類中,讓它們產生愛的結晶。或者,能夠嘗試運用 隱藏委託關係(Hide Delegate)
讓另外一個類來爲它們牽線搭橋。
繼承每每形成類之間過度緊密,由於子類對超類的瞭解老是超事後者的主觀願望,若是你以爲該讓這個子類本身闖蕩,請運用 以委託取代繼承(Replace Inheritance with Delegation)
來讓超類和子類分家。
問題
你的程序中,有個函數與其所駐類以外的另外一個類進行更多交流:調用後者,或被後者調用。
解決
在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是舊函數徹底移除。
問題
在你的程序中,某個字段被其所駐類以外的另外一個類更多地用到。
解決
在目標類新建一個字段,修改源字段的全部用戶,令他們改用新字段。
問題
兩個類之間有雙向關聯,但其中一個類現在再也不須要另外一個類的特性。
解決
去除沒必要要的關聯。
問題
某個類作了不止一件事。
解決
創建一個新類,將相關的字段和函數從舊類搬移到新類。
問題
客戶經過一個委託類來調用另外一個對象。
解決
在服務類上創建客戶所需的全部函數,用以隱藏委託關係。
問題
某個子類只使用超類接口中的一部分,或是根本不須要繼承而來的數據。
解決
在子類中新建一個字段用以保存超類;調整子類函數,令它改而委託超類;而後去掉二者之間的繼承關係。
過分耦合的消息鏈(Message Chains)
消息鏈的形式相似於:
obj.getA().getB().getC()
。
若是你看到用戶向一個對象請求另外一個對象,而後再向後者請求另外一個對象,而後再請求另外一個對象……這就是消息鏈。實際代碼中你看到的多是一長串 getThis()或一長串臨時變量。採起這種方式,意味客戶代碼將與查找過程當中的導航緊密耦合。一旦對象間關係發生任何變化,客戶端就不得不作出相應的修改。
隱藏委託關係(Hide Delegate)
刪除一個消息鏈。提煉函數(Extract Method)
把使用該對象的代碼提煉到一個獨立函數中,再運用 搬移函數(Move Method)
把這個函數推入消息鏈。問題
客戶經過一個委託類來調用另外一個對象。
解決
在服務類上創建客戶所需的全部函數,用以隱藏委託關係。
問題
你有一段代碼能夠組織在一塊兒。
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
複製代碼
解決
移動這段代碼到一個新的函數中,使用函數的調用來替代老代碼。
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
複製代碼
問題
你的程序中,有個函數與其所駐類以外的另外一個類進行更多交流:調用後者,或被後者調用。
解決
在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是舊函數徹底移除。