refacforing

重構 - 改善既有程式的設計 - Organizing Data

這篇文章討論《重構 - 改善既有程式的設計》裡的第八章 - Organizing Data

主要會討論程式碼的壞味道中 沒有被提及的重構方法裡面有關於 重新組織資料 的內容

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

重新組織資料

Replace Data Value with Object

當你有一筆資料項(data item) 需要額外的資料和行為 就把那個資料項變成一個物件

Change Value To Reference

將實質物件改為引用物件

Alt text

上圖的意思 就是把一個value object轉成reference object

Alt text

別激動別激動 我一開始看也是看不懂 我來說說我的理解吧

我們繼續使用Replace Data Value with Object的例子 在使用Replace Data Value with Object重構之後 每張Order上都有一個Customer物件而不是String

class Order{
  private Customer customer;
  public String getCustomerName() {
    return customer.getName();
  }
  public void setCustomer(String customerName) {
    customer = new Customer(customerName);
  }
  public Order(String customerName) {
    customer = new Customer(customerName);
  }
}

但當Order多了之後 發現很多Customer都是同一個人(同一個名字) 但同一個人在不同的Order物件裡面卻是不同的Customer物件 之後管理起來會麻煩 比如顧客換名字或是換地址 就得改所有的Order物件

你說這還不簡單 建構子跟setter裡面 傳進來Customer物件不就得了?

class Order{
  private Customer customer;
  public String getCustomerName() {
    return customer.getName();
  }
  public void setCustomer(Customer c) {
    customer = c;
  }
  public Order(Customer c) {
    customer = c;
  }
}

這的確是把實質物件改為引用物件 但你的使用者使用起來還是很麻煩 他在創建一個新Order的時候 手上拿著customerName 你要先找出有著這個名字的Customer物件 才能把這個物件丟進Order的constructor

好麻煩 怎麼辦呢

我們使用Replace Constructor with Factory Method 我們就可以控制Customer的創建過程

class Customer {
  //用一個hash table紀錄目前有的顧客
  private static HashMap<String, Customer> map = new HashMap<>();
  
  public static Customer create(String name) {
    if(!map.containsKey(name)) {
      map.put(name, new Customer(name));
    }
    return map.get(name);
  }

  private final String name;
}

有了create這個靜態工廠之後 Order只要有customerName 就很好辦了

class Order {
  //…
  private Customer customer;
  public String getCustomerName() {
      return customer.getName();
  }
  public void setCustomer(String customerName) {
    customer = Customer.create(customerName);
  }
  public Order(String customerName) {
    customer = Customer.create(customerName);
  }
}

Change Reference To Value

[Change Value To Reference]的反向 如果你的reference object很小而且不可變 那就不如把它換成value object

Replace Array with Object

當你有一個陣列 其中的元素代表不同的東西 那就用物件來替換

Change Bidirectional Association to Unidirectional

如果兩個類別A,B 只有類別A需要知道類別B的特性 那就把依賴關係變成單向

Change Unidirectional Association to Bidirectional

Change Bidirectional Association to Unidirectional的反向

當兩個類別彼此需要知道對方特性 但目前只有一個方向的依賴 就把依賴變成雙向

Alt text

雖然比較複雜 但卻是比較常見的use case 你會想知道每個Order是由哪個Customer下訂的 你當然也會想知道每個Customer有下了哪些Order

Replace Magic Number with Symbolic Constant

為一個有意義的數值命名

double potentialEnergy(double mass, double height) {
  return mass * height * 9.81;
}

把重力常數命名一下

static final double GRAVITATIONAL_CONSTANT = 9.81;

double potentialEnergy(double mass, double height) {
  return mass * height * GRAVITATIONAL_CONSTANT;
}

Encapsulate Field

封裝欄位 把public的欄位變成private 並提供存取函式

Encapsulate Collection

檢查是不是對於Collections也都做好了封裝

Replace Type Code with Class

Type Code三兄弟之老大: 類別之中有一個數值型別代碼(numeric type code) 但他不影響類別的行為

Replace Type Code with Subclasses

Type Code三兄弟之老二: 當類別之中的數值型別代碼會影響類別行為的時候 就用這個方法

Replace Type Code with State/Strategy

Type Code三兄弟之老三: 當你想動態的改變類別的行為的時候 就用這個方法

Replace Subclass with Fields

你的各個subclass的差別 只差在回傳常數資料的函式 你就可以把這個函式提煉到superClass 然後移除所有subClass

Alt text