:notebook: 本文已歸檔到:「blog」java
翻譯自:https://sourcemaking.com/refactoring/smells/dispensablesgit
非必要的(Dispensables)這組壞味道意味着:這樣的代碼無關緊要,它的存在反而影響總體代碼的整潔和可讀性。程序員
冗餘類(Lazy Class)github
理解和維護老是費時費力的。若是一個類不值得你花費精力,它就應該被刪除。算法
也許一個類的初始設計是一個功能徹底的類,然而隨着代碼的變遷,變得沒什麼用了。 又或者類起初的設計是爲了支持將來的功能擴展,然而卻一直未派上用場。設計模式
將類內聯化(Inline Class)
來幹掉。摺疊繼承體系(Collapse Hierarchy)
。問題bash
某個類沒有作太多事情。框架
解決ide
將這個類的全部特性搬移到另外一個類中,而後移除原類。函數
問題
超類和子類之間無太大區別。
解決
將它們合爲一體。
誇誇其談將來性(Speculative Generality)
存在未被使用的類、函數、字段或參數。
有時,代碼僅僅爲了支持將來的特性而產生,然而卻一直未實現。結果,代碼變得難以理解和維護。
摺疊繼承體系(Collapse Hierarch)
。將類內聯化(Inline Class)
消除。內聯函數(Inline Method)
消除。移除參數(Remove Parameter)
消除。問題
超類和子類之間無太大區別。
解決
將它們合爲一體。
問題
某個類沒有作太多事情。
解決
將這個類的全部特性搬移到另外一個類中,而後移除原類。
問題
一個函數的本體比函數名更清楚易懂。
class PizzaDelivery {
//...
int getRating() {
return moreThanFiveLateDeliveries() ? 2 : 1;
}
boolean moreThanFiveLateDeliveries() {
return numberOfLateDeliveries > 5;
}
}
複製代碼
解決
在函數調用點插入函數本體,而後移除該函數。
class PizzaDelivery {
//...
int getRating() {
return numberOfLateDeliveries > 5 ? 2 : 1;
}
}
複製代碼
問題
函數本體再也不須要某個參數。
解決
將該參數去除。
純稚的數據類(Data Class)
指的是隻包含字段和訪問它們的 getter 和 setter 函數的類。這些僅僅是供其餘類使用的數據容器。這些類不包含任何附加功能,而且不能對本身擁有的數據進行獨立操做。
當一個新建立的類只包含幾個公共字段(甚至可能幾個 getters / setters)是很正常的。可是對象的真正力量在於它們能夠包含做用於數據的行爲類型或操做。
封裝字段(Encapsulated Field)
來隱藏字段的直接訪問方式。封裝集合(Encapsulated Collection)
把它們封裝起來。搬移函數(Move Method)
把那些調用行爲搬移到 純稚的數據類(Data Class)
來。若是沒法搬移這個函數,就運用 提煉函數(Extract Method)
產生一個可搬移的函數。移除設置函數(Remove Setting Method)
和 隱藏函數(Hide Method)
。問題
你的類中存在 public 字段。
class Person {
public String name;
}
複製代碼
解決
將它聲明爲 private,並提供相應的訪問函數。
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String arg) {
name = arg;
}
}
複製代碼
問題
有個函數返回一個集合。
解決
讓該函數返回該集合的一個只讀副本,並在這個類中提供添加、移除集合元素的函數。
問題
你的程序中,有個函數與其所駐類以外的另外一個類進行更多交流:調用後者,或被後者調用。
解決
在該函數最常引用的類中創建一個有着相似行爲的新函數。將舊函數變成一個單純的委託函數,或是舊函數徹底移除。
問題
你有一段代碼能夠組織在一塊兒。
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);
}
複製代碼
問題
類中的某個字段應該在對象建立時被設值,而後就再也不改變。
解決
去掉該字段的全部設值函數。
問題
有一個函數,歷來沒有被其餘任何類用到。
解決
將這個函數修改成 private。
過多的註釋(Comments)
註釋自己並非壞事。可是經常有這樣的狀況:一段代碼中出現長長的註釋,而它之因此存在,是由於代碼很糟糕。
註釋的做者意識到本身的代碼不直觀或不明顯,因此想使用註釋來講明本身的意圖。這種狀況下,註釋就像是爛代碼的除臭劑。
最好的註釋是爲函數或類起一個恰當的名字。
若是你以爲一個代碼片斷沒有註釋就沒法理解,請先嚐試重構,試着讓全部註釋都變得多餘。
提煉變量(Extract Variable)
將表達式切分爲易理解的子表達式。提煉函數(Extract Method)
。函數更名(Rename Method)
來爲函數起一個能夠自解釋的名字。引入斷言(Introduce Assertion)
。註釋有時候頗有用:
問題
你有個難以理解的表達式。
void renderBanner() {
if ((platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
wasInitialized() && resize > 0 )
{
// do something
}
}
複製代碼
解決
將表達式的結果或它的子表達式的結果用不言自明的變量來替代。
void renderBanner() {
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIE = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIE && wasInitialized() && wasResized) {
// do something
}
}
複製代碼
問題
你有一段代碼能夠組織在一塊兒。
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);
}
複製代碼
問題
函數的名稱未能恰當的揭示函數的用途。
class Person {
public String getsnm();
}
複製代碼
解決
修改函數名。
class Person {
public String getSecondName();
}
複製代碼
問題
某一段代碼須要對程序狀態作出某種假設。
double getExpenseLimit() {
// should have either expense limit or a primary project
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
複製代碼
解決
以斷言明確表現這種假設。
double getExpenseLimit() {
Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
複製代碼
注:請不要濫用斷言。不要使用它來檢查」應該爲真「的條件,只能使用它來檢查「必定必須爲真」的條件。實際上,斷言更可能是用於自我檢測代碼的一種手段。在產品真正交付時,每每都會消除全部斷言。
重複代碼(Duplicate Code)
重複代碼堪稱爲代碼壞味道之首。消除重複代碼老是有利無害的。
重複代碼一般發生在多個程序員同時在同一程序的不一樣部分上工做時。因爲他們正在處理不一樣的任務,他們可能不知道他們的同事已經寫了相似的代碼。
還有一種更隱晦的重複,特定部分的代碼看上去不一樣但實際在作同一件事。這種重複代碼每每難以找到和消除。
有時重複是有目的性的。當急於知足 deadline,而且現有代碼對於要交付的任務是「幾乎正確的」時,新手程序員可能沒法抵抗複製和粘貼相關代碼的誘惑。在某些狀況下,程序員只是太懶惰。
提煉函數(Extract Method)
提煉出重複的代碼,而後讓這兩個地點都調用被提煉出來的那段代碼。提煉函數(Extract Method)
,而後對被提煉出來的函數運用 函數上移(Pull Up Method)
,將它推入超類。構造函數本體上移(Pull Up Constructor Body)
。塑造模板函數(Form Template Method)
得到一個 模板方法模式(Template Method) 。替換算法(Substitute Algorithm)
將其餘函數的算法替換掉。提煉超類(Extract Superclass)
,以便爲維護全部先前功能的這些類建立一個超類。提煉類(Extract Class)
,並在另外一個類中使用這個新的組件。合併條件表達式(Consolidate Conditional Expression)
將這些操做合併爲單個條件,並運用 提煉函數(Extract Method)
將該條件放入一個名字容易理解的獨立函數中。合併重複的條件片斷(Consolidate Duplicate Conditional Fragments)
將它們都存在的代碼片斷置於條件表達式外部。問題
你有一段代碼能夠組織在一塊兒。
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);
}
複製代碼
問題
有些函數,在各個子類中產生徹底相同的結果。
解決
將該函數移至超類。
問題
你在各個子類中擁有一些構造函數,它們的本體幾乎徹底一致。
class Manager extends Employee {
public Manager(String name, String id, int grade) {
this.name = name;
this.id = id;
this.grade = grade;
}
//...
}
複製代碼
解決
在超類中新建一個構造函數,並在子類構造函數中調用它。
class Manager extends Employee {
public Manager(String name, String id, int grade) {
super(name, id);
this.grade = grade;
}
//...
}
複製代碼
問題
你有一些子類,其中相應的某些函數以相同的順序執行相似的操做,但各個操做的細節上有所不一樣。
解決
將這些操做分別放進獨立函數中,並保持它們都有相同的簽名,因而原函數也就變得相同了。而後將原函數上移至超類。
注:這裏只提到具體作法,建議瞭解一下模板方法設計模式。
問題
你想要把某個算法替換爲另外一個更清晰的算法。
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals("Don")){
return "Don";
}
if (people[i].equals("John")){
return "John";
}
if (people[i].equals("Kent")){
return "Kent";
}
}
return "";
}
複製代碼
解決
將函數本體替換爲另外一個算法。
String foundPerson(String[] people){
List candidates =
Arrays.asList(new String[] {"Don", "John", "Kent"});
for (int i=0; i < people.length; i++) {
if (candidates.contains(people[i])) {
return people[i];
}
}
return "";
}
複製代碼
問題
兩個類有類似特性。
解決
爲這兩個類創建一個超類,將相同特性移至超類。
問題
某個類作了不止一件事。
解決
創建一個新類,將相關的字段和函數從舊類搬移到新類。
問題
你有一系列條件分支,都獲得相同結果。
double disabilityAmount() {
if (seniority < 2) {
return 0;
}
if (monthsDisabled > 12) {
return 0;
}
if (isPartTime) {
return 0;
}
// compute the disability amount
//...
}
複製代碼
解決
將這些條件分支合併爲一個條件,並將這個條件提煉爲一個獨立函數。
double disabilityAmount() {
if (isNotEligableForDisability()) {
return 0;
}
// compute the disability amount
//...
}
複製代碼
問題
在條件表達式的每一個分支上有着相同的一段代碼。
if (isSpecialDeal()) {
total = price * 0.95;
send();
}
else {
total = price * 0.98;
send();
}
複製代碼
解決
將這段重複代碼搬移到條件表達式以外。
if (isSpecialDeal()) {
total = price * 0.95;
}
else {
total = price * 0.98;
}
send();
複製代碼