范例(Examples)
本例從Change Unidirectional association to Bidirectional(197)留下的代碼開始進行,其中Customer和Order之間有雙向關聯:
class Order...
Customer getCustomer() {
return _customer;
}
void setCustomer(Custoemr arg) ...
if(_customer != null) _customer.friendOrders().remove(this);
_customer = arg;
if(_customer != null) _customer.friendOrders().add(this);
}
private Customer _customer; //譯注:這是Order-to-Customer link也是本例的移除對象.
class Customer ...
void addOrder(Order arg) {
arg.setCustomer(this);
}
private Set _orders = new HashSet();
//譯注:以上是Customer-to-Order link
Set friendOrders() {
return _orders;
}
后來我發現,除非先有Customer對象,否則不會存在Order對象.因此我想將[從Order到Customer的連接]移除掉.
對于本項重構來說,最困難的就是檢查可行性.如果我知道本項重構是安全的,那么重構手法自身十分簡單.問題在于是否有任何代碼倚賴_customer值域的存在.如果確實有,那么在刪除這個值域之后,我必須提供替代品.
首先,我需要研究所有讀取這個值域的函數,以及所有使用這些函數的函數.我能找到另一條途徑來供應Customer對象嗎----這通常意味將Customer對象作為引數(argument)傳遞給其用戶(某函數).下面是一個簡化例子:
class Order...
double getDiscountedPrice() {
return getGrossPrice() * (1 - _customer.getDiscount());
}
改變為:
class Order...
double getDiscountedPrice(Customer customer) {
return getGrossPrice() * (1 - customer.getDiscount());
}
如果待改函數是被Customer對象調用的,那么這樣的修改方案特別容易實施,因為Customer對象將自己作為引數(argument)傳給函數很是容易.所以下列代碼:
class Customer...
double getPriceFor(Order order) {
Assert.isTrue(_orders.contains(order)); //see Introduce Assertion(267)
return order.getDiscountedPrice();
變成了:
class Customer...
double getPriceFor(Order order) {
Assert.isTrue(_orders.contains(order));
return order.getDiscountedPrice(this);
另一個作法就是修改取值函數(getter),使其在不使用_customer值域的前提下返回一個Customer對象.如果這行得通,我就可以使用Substitute Algorithm(139)修改Order.getCustomer()函數算法.我有可能這樣修改代碼:
Customer getCustomer() {
Iterator iter = Customer.getInstance().iterator();
while(iter.hasNext()) {
Customer each = (Customer)iter.next();
if(each.containsOrder(this) return each;
}
return null;
}
這段代碼比較慢,不過確實可行.而且,在數據庫環境下,如果我需要使用數據庫查詢語句,這段代碼對系統性能的影響可能并不顯著.如果,Order class中有些函數使用_customer值域,我可以實施Self Encapsulate Field(171)令它們轉而改用上述的getCustomer()函數.
如果我要保留上述的取值函數(getter),那么Order和Customer的關聯從接口上看雖然仍然是雙向,但實現上已經是單向關系了.雖然我移除了反向指針,但兩個classes彼此之間的依存關系(inter-dependencies)仍然存在.
如果我要替換取值函數(getter),那么我就專注地替換它,其他部分留待以后處理.我會逐一修改取值函數的調用者.讓它們通過其他來源取得Customer對象.每次修改后都編譯并測試.實際工作中這一過程往往相當快.如果這個過程讓我覺得很棘手很復雜,我會放棄本項重構.
一旦我消除了_customer值域的所有讀取點,我就可以著手處理[對此值域進行賦值動作]的函數了.很簡單,只要把這些賦值動作全部移除,再把值域一并刪除,就行了.由于已經沒有任何代碼需要這個值域,所以刪掉它并不會帶來任何影響.
本例從Change Unidirectional association to Bidirectional(197)留下的代碼開始進行,其中Customer和Order之間有雙向關聯:
class Order...
Customer getCustomer() {
return _customer;
}
void setCustomer(Custoemr arg) ...
if(_customer != null) _customer.friendOrders().remove(this);
_customer = arg;
if(_customer != null) _customer.friendOrders().add(this);
}
private Customer _customer; //譯注:這是Order-to-Customer link也是本例的移除對象.
class Customer ...
void addOrder(Order arg) {
arg.setCustomer(this);
}
private Set _orders = new HashSet();
//譯注:以上是Customer-to-Order link
Set friendOrders() {
return _orders;
}
后來我發現,除非先有Customer對象,否則不會存在Order對象.因此我想將[從Order到Customer的連接]移除掉.
對于本項重構來說,最困難的就是檢查可行性.如果我知道本項重構是安全的,那么重構手法自身十分簡單.問題在于是否有任何代碼倚賴_customer值域的存在.如果確實有,那么在刪除這個值域之后,我必須提供替代品.
首先,我需要研究所有讀取這個值域的函數,以及所有使用這些函數的函數.我能找到另一條途徑來供應Customer對象嗎----這通常意味將Customer對象作為引數(argument)傳遞給其用戶(某函數).下面是一個簡化例子:
class Order...
double getDiscountedPrice() {
return getGrossPrice() * (1 - _customer.getDiscount());
}
改變為:
class Order...
double getDiscountedPrice(Customer customer) {
return getGrossPrice() * (1 - customer.getDiscount());
}
如果待改函數是被Customer對象調用的,那么這樣的修改方案特別容易實施,因為Customer對象將自己作為引數(argument)傳給函數很是容易.所以下列代碼:
class Customer...
double getPriceFor(Order order) {
Assert.isTrue(_orders.contains(order)); //see Introduce Assertion(267)
return order.getDiscountedPrice();
變成了:
class Customer...
double getPriceFor(Order order) {
Assert.isTrue(_orders.contains(order));
return order.getDiscountedPrice(this);
另一個作法就是修改取值函數(getter),使其在不使用_customer值域的前提下返回一個Customer對象.如果這行得通,我就可以使用Substitute Algorithm(139)修改Order.getCustomer()函數算法.我有可能這樣修改代碼:
Customer getCustomer() {
Iterator iter = Customer.getInstance().iterator();
while(iter.hasNext()) {
Customer each = (Customer)iter.next();
if(each.containsOrder(this) return each;
}
return null;
}
這段代碼比較慢,不過確實可行.而且,在數據庫環境下,如果我需要使用數據庫查詢語句,這段代碼對系統性能的影響可能并不顯著.如果,Order class中有些函數使用_customer值域,我可以實施Self Encapsulate Field(171)令它們轉而改用上述的getCustomer()函數.
如果我要保留上述的取值函數(getter),那么Order和Customer的關聯從接口上看雖然仍然是雙向,但實現上已經是單向關系了.雖然我移除了反向指針,但兩個classes彼此之間的依存關系(inter-dependencies)仍然存在.
如果我要替換取值函數(getter),那么我就專注地替換它,其他部分留待以后處理.我會逐一修改取值函數的調用者.讓它們通過其他來源取得Customer對象.每次修改后都編譯并測試.實際工作中這一過程往往相當快.如果這個過程讓我覺得很棘手很復雜,我會放棄本項重構.
一旦我消除了_customer值域的所有讀取點,我就可以著手處理[對此值域進行賦值動作]的函數了.很簡單,只要把這些賦值動作全部移除,再把值域一并刪除,就行了.由于已經沒有任何代碼需要這個值域,所以刪掉它并不會帶來任何影響.