基本類型偏執(Primitive Obsession)程序員
- 使用基本類型而不是小對象來實現簡單任務(例如貨幣、範圍、電話號碼字符串等)。
- 使用常量編碼信息(例如一個用於引用管理員權限的常量
USER_ADMIN_ROLE = 1
)。- 使用字符串常量做爲字段名在數組中使用。
相似其餘大部分壞味道,基本類型偏執誕生於類初建的時候。一開始,可能只是很少的字段,隨着表示的特性愈來愈多,基本數據類型字段也愈來愈多。算法
基本類型經常被用於表示模型的類型。你有一組數字或字符串用來表示某個實體。數據庫
還有一個場景:在模擬場景,大量的字符串常量被用於數組的索引。編程
大多數編程語言都支持基本數據類型和結構類型(類、結構體等)。結構類型容許程序員將基本數據類型組織起來,以表明某一事物的模型。設計模式
基本數據類型能夠當作是機構類型的積木塊。當基本數據類型數量成規模後,將它們有組織地結合起來,能夠更方便的管理這些數據。數組
以類取代類型碼(Replace Type Code with Class)
。引入參數對象(Introduce Parameter Object)
或 保持對象完整(Preserve Whole Object)
。以類取代類型碼(Replace Type Code with Class)
將它替換掉。若是你有與類型碼相關的條件表達式,可運用 以子類取代類型碼(Replace Type Code with Subclass)
或 以狀態/策略模式取代類型碼(Replace Type Code with State/Strategy)
加以處理。以對象取代數組(Replace Array with Object)
。問題bash
類之中有一個數值類型碼,但它並不影響類的行爲。session
解決編程語言
以一個新的類替換該數值類型碼。函數
問題
某些參數老是很天然地同時出現。
解決
以一個對象來取代這些參數。
問題
你從某個對象中取出若干值,將它們做爲某一次函數調用時的參數。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);複製代碼
解決
改成傳遞整個對象。
boolean withinPlan = plan.withinRange(daysTempRange);複製代碼
問題
你有一個不可變的類型碼,它會影響類的行爲。
解決
以子類取代這個類型碼。
問題
你有一個類型碼,它會影響類的行爲,但你沒法經過繼承消除它。
解決
以狀態對象取代類型碼。
問題
你有一個數組,其中的元素各自表明不一樣的東西。
String[] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";複製代碼
解決
以對象替換數組。對於數組中的每一個元素,以一個字段來表示。
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");複製代碼
數據泥團(Data Clumps)
有時,代碼的不一樣部分包含相同的變量組(例如用於鏈接到數據庫的參數)。這些綁在一塊兒出現的數據應該擁有本身的對象。
一般,數據泥團的出現時由於糟糕的編程結構或「複製-粘貼式編程」。
有一個判斷是不是數據泥團的好辦法:刪掉衆多數據中的一項。這麼作,其餘數據有沒有於是失去意義?若是它們再也不有意義,這就是個明確的信號:你應該爲它們產生一個新的對象。
提煉類(Extract Class)
將它們提煉到一個獨立對象中。引入參數對象(Introduce Parameter Object)
將它們組織成一個類。保持對象完整(Preserve Whole Object)
將整個數據對象傳入到函數中。問題
某個類作了不止一件事。
解決
創建一個新類,將相關的字段和函數從舊類搬移到新類。
問題
某些參數老是很天然地同時出現。
解決
以一個對象來取代這些參數。
問題
你從某個對象中取出若干值,將它們做爲某一次函數調用時的參數。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);複製代碼
解決
改成傳遞整個對象。
boolean withinPlan = plan.withinRange(daysTempRange);複製代碼
過大的類(Large Class)
一個類含有過多字段、函數、代碼行。
類一般一開始很小,可是隨着程序的增加而逐漸膨脹。
相似於過長函數,程序員一般以爲在一個現存類中添加新特性比建立一個新的類要容易。
設計模式中有一條重要原則:職責單一原則。一個類應該只賦予它一個職責。若是它所承擔的職責太多,就該考慮爲它減減負。
提煉類(Extract Class)
。提煉子類(Extract Subclass)
。提煉接口(Extract Interface)
。複製被監視數據(Duplicate Observed Data)
能夠告訴你怎麼作。問題
某個類作了不止一件事。
解決
創建一個新類,將相關的字段和函數從舊類搬移到新類。
問題
一個類中有些特性僅用於特定場景。
解決
建立一個子類,並將用於特殊場景的特性置入其中。
問題
多個客戶端使用一個類部分相同的函數。另外一個場景是兩個類中的部分函數相同。
解決
移動相同的部分函數到接口中。
問題
若是存儲在類中的數據是負責 GUI 的。
解決
一個比較好的方法是將負責 GUI 的數據放入一個獨立的類,以確保 GUI 數據與域類之間的鏈接和同步。
過長函數(Long Method)
一個函數含有太多行代碼。通常來講,任何函數超過 10 行時,你就能夠考慮是否是過長了。 函數中的代碼行數原則上不要超過 100 行。
一般狀況下,建立一個新函數的難度要大於添加功能到一個已存在的函數。大部分人都以爲:「我就添加這麼兩行代碼,爲此新建一個函數實在是小題大作了。」因而,張三加兩行,李四加兩行,王五加兩行。。。函很多天益龐大,最終爛的像一鍋漿糊,再也沒人能徹底看懂了。因而你們就更不敢輕易動這個函數了,只能惡性循環的往其中添加代碼。因此,若是你看到一個超過 200 行的函數,一般都是多個程序員東拼西湊出來的。
一個很好的技巧是:尋找註釋。添加註釋,通常有這麼幾個緣由:代碼邏輯較爲晦澀或複雜;這段代碼功能相對獨立;特殊處理。 若是代碼前方有一行註釋,就是在提醒你:能夠將這段代碼替換成一個函數,並且能夠在註釋的基礎上給這個函數命名。若是函數有一個描述恰當的名字,就不須要去看內部代碼到底是如何實現的。就算只有一行代碼,若是它須要以註釋來講明,那也值得將它提煉到獨立函數中。
提煉函數(Extract Method)
。以查詢取代臨時變量(Replace Temp with Query)
,引入參數對象(Introduce Parameter Object)
或 保持對象完整(Preserve Whole Object)
。以函數對象取代函數(Replace Method with Method Object)
嘗試移動整個函數到一個獨立的對象中。分解條件表達式(Decompose Conditional)
。至於循環,應該使用 提煉函數(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);
}複製代碼
問題
將表達式的結果放在局部變量中,而後在代碼中使用。
double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}複製代碼
解決
將整個表達式移動到一個獨立的函數中並返回結果。使用查詢函數來替代使用變量。若是須要,能夠在其餘函數中合併新函數。
double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}複製代碼
問題
某些參數老是很天然地同時出現。
解決
以一個對象來取代這些參數。
問題
你從某個對象中取出若干值,將它們做爲某一次函數調用時的參數。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);複製代碼
解決
改成傳遞整個對象。
boolean withinPlan = plan.withinRange(daysTempRange);複製代碼
問題
你有一個過長函數,它的局部變量交織在一塊兒,以至於你沒法應用提煉函數(Extract Method) 。
class Order {
//...
public double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation.
//...
}
}複製代碼
解決
將函數移到一個獨立的類中,使得局部變量成了這個類的字段。而後,你能夠將函數分割成這個類中的多個函數。
class Order {
//...
public double price() {
return new PriceCalculator(this).compute();
}
}
class PriceCalculator {
private double primaryBasePrice;
private double secondaryBasePrice;
private double tertiaryBasePrice;
public PriceCalculator(Order order) {
// copy relevant information from order object.
//...
}
public double compute() {
// long computation.
//...
}
}複製代碼
問題
你有複雜的條件表達式。
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
}
else {
charge = quantity * summerRate;
}複製代碼
解決
根據條件分支將整個條件表達式分解成幾個函數。
if (notSummer(date)) {
charge = winterCharge(quantity);
}
else {
charge = summerCharge(quantity);
}複製代碼
過長參數列(Long Parameter List)
一個函數有超過 三、4 個入參。
過長參數列多是將多個算法併到一個函數中時發生的。函數中的入參能夠用來控制最終選用哪一個算法去執行。
過長參數列也多是解耦類之間依賴關係時的副產品。例如,用於建立函數中所需的特定對象的代碼已從函數移動到調用函數的代碼處,但建立的對象是做爲參數傳遞到函數中。所以,原始類再也不知道對象之間的關係,而且依賴性也已經減小。可是若是建立的這些對象,每個都將須要它本身的參數,這意味着過長參數列。
太長的參數列難以理解,太多參數會形成先後不一致、不易使用,並且一旦須要更多數據,就不得不修改它。
以函數取代參數(Replace Parameter with Methods)
。在這裏,,「已有的對象」多是函數所屬類裏的一個字段,也多是另外一個參數。保持對象完整(Preserve Whole Object)
未來自同一對象的一堆數據收集起來,並以該對象替換它們。引入參數對象(Introduce Parameter Object)
爲它們製造出一個「參數對象」。問題
對象調用某個函數,並將所得結果做爲參數,傳遞給另外一個函數。而接受該參數的函數自己也可以調用前一個函數。
int basePrice = quantity * itemPrice;
double seasonDiscount = this.getSeasonalDiscount();
double fees = this.getFees();
double finalPrice = discountedPrice(basePrice, seasonDiscount, fees);複製代碼
解決
讓參數接受者去除該項參數,並直接調用前一個函數。
int basePrice = quantity * itemPrice;
double finalPrice = discountedPrice(basePrice);複製代碼
問題
你從某個對象中取出若干值,將它們做爲某一次函數調用時的參數。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);複製代碼
解決
改成傳遞整個對象。
boolean withinPlan = plan.withinRange(daysTempRange);複製代碼
問題
某些參數老是很天然地同時出現。
解決
以一個對象來取代這些參數。