refacforing

重構 - 改善既有程式的設計 - Making Method Calls Simpler

這篇文章討論《重構 - 改善既有程式的設計》裡的第十章 - Making Method Calls Simpler

主要會討論程式碼的壞味道中 沒有被提及的重構方法裡面有關於 簡化函式呼叫 的內容

圖片以及程式碼來源自重構 - 改善既有程式的設計

簡化函式呼叫

在物件導向的技術中 最重要的概念就是介面 容易被理解和使用的介面 是開發良好物件導向軟體的關鍵 本章會介紹所有讓介面變得更簡潔易用的重構方法

Rename Method

函式的名稱就應該顯示函式的用途

Separate Query From Modifier

當你有個函式 技回傳物件狀態 也修改物件狀態 那就最好分開 一個負責查詢 一個負責修改

Alt text

一個常見的好習慣是區分出 有副作用的 和 無副作用的 函式

Parameterize Method

你可能在重構的過程中發現很多類似的函式 只是差別差在一個數字 而且函式的實作中 對於每個數字的處理方式都一樣 那就把這個變量提出來 讓函式攜帶參數

Alt text

Replace Parameter with Explicit Methods

Parameterize Method的反向 把參數取消 分離成不同的函式

什麼時候使用哪個呢 如果函式的實作中 對於每個數字的處理方式不一樣 而且還有一個if-else 那就很值得分離

來看個小例子

void setHeightToTwo(){
  a.setCurrentHieght(2);
}
void setHeightToFive(){
  a.setCurrentHieght(5);
}
void setHeightToTen(){
  a.setCurrentHieght(10);
}

這個例子就很適合加個參數

void setHeight(int h){
  a.setCurrentHieght(h)
}

但如果是下面的情況

void setHeight(int h){
  if(h == 2) {
    methodA();
  }
  if(h == 5) {
    methodB();
  }
  if(h == 10) {
    methodC();
  }
}

那就應該把參數拿掉

void setHeightToTwo(){
  methodA();
}
void setHeightToFive(){
  methodB();
}
void setHeightToTen(){
  methodC();
}

Preserve Whole Object

你從一個物件中取出某些值 去傳給其他函式 那你不如直接把那個物件丟進去

Replace Parameter with Method

如果被呼叫端也可以自己拿到那個參數 就可以直接讓被呼叫端自己算

Introduce Parameter Object

如果參數間的關係很強 可以把參數們包成一個參數物件

Remove Setting Method

如果你的物件只應該在初始時被設值 那你就應該把setter拿掉

Hide Method

如果有一個函式 從來沒有被其他類別用到的話 就應該把它改成private

可以參考Effective Java Item15 - 使類和成員的可訪問性最小化

Replace Constructor with Factory Method

請參考Effective Java Item1 - 靜態工廠方法 好處非常多

Replace Error Code with Exception

如果你有一個函式 使用回傳值來表示出了什麼錯 那你不如直接拋出例外

int withdraw(int amount) {
  if (amount > _balance) {
    return -1;
  }
  else {
    balance -= amount;
    return 0;
  }
}

不如這樣

void withdraw(int amount) throws BalanceException {
  if (amount > _balance) {
    throw new BalanceException();
  }
  balance -= amount;
}

Replace Exception with Test

不要濫用異常 如果這個異常的程式碼可以被輕易的避免

比如你有以下的程式

double getValueForPeriod(int periodNumber) {
  try {
    return values[periodNumber];
  } catch (ArrayIndexOutOfBoundsException e) {
    return 0;
  }
}

你只要確認periodNumber是不是出界就可以

double getValueForPeriod(int periodNumber) {
  if (periodNumber >= values.length) {
    return 0;
  }
  return values[periodNumber];
}