如何避免類之間的循環依賴


      最近在看《Java應用架構設計 模塊化模式與OSGi》,深有感觸,在此做些總結。(電子版可以在Java1234.com上下載到)

      在使用Java開發中,各種依賴不可避免。比如類之間的繼承,jar包之間的相互依賴。依賴在某種程度上不可避免,但是過多的依賴勢必會增加系統的復雜性,使代碼難以閱讀,從而成為團隊開發的阻礙。循環依賴尤其糟糕。

     循環依賴存在與多種實體之間,尤其是類、包、模塊之間。當兩個類相互引用時,就會出現循環依賴。下面摘抄書中的一個例子。

     如圖1.1所示,有Customer和Bill兩個類。在本例中,Customer有一個Bill的實例列表,而Bill實例也引用Customer來計算折扣總額。這也成為雙向關聯(bidirectional association)。對於維護和測試,這將是一個將是一個問題,因為在不引用另一個類的情況下,你不能單獨的對其中一個類做任何事情。

  image

圖1.1 類之間的循環依賴

      代碼清單1.1展示了Customer類,代碼清單1.2展示了Bill類。(為了簡化,每個類的特定部分進行了省略。)在這里清楚展示了循環依賴。  

package com.scott.cust;

import java.util.*;
import java.math.BigDecimal;
import com.scott.bill.*;

public class Customer {
   private List<Bill> bills;
   //特定Customer的折扣根據訂單數目計算
   public BigDecimal getDiscountAmount() {
       if (bills.size() > 5) {
           return new BigDecimal(0.1);
       } else {
           return new BigDecimal(0.03);
       }
   }
   
   public void createBill() {
       Bill bill = new Bill(this);
       if (bills == null) {
           bills = new ArrayList<Bill>();
       }
       bills.add(bill);
   }

}
代碼清單1.1 Customer
package com.scott.bill;

import com.scott.cust.*;
import java.math.BigDecimal;

public class Bill {
   private Customer customer;
   
   public Bill(Customer customer) {
        this.customer = customer;   
   }
   
   public BigDecimal pay() {
       BigDecimal discount =  new BigDecimal(1),subtract(
            this.customer.getDiscountAmount()).setScale(2,
            BigDecimal.ROUND_HALF_UP);
       //確認折扣和應付款代碼省略
       return paidAmount;

  }

}
代碼清單1.2 Bill

      可以有多種方式打破循環依賴(筆者目前所知就是引入抽象),其中之一就是引入抽象,如圖1.2所示。現在,借助mock的DiscountCaculator,Bill就可以容易的進行(單元)測試了。當然,測試Customer依舊需要Bill的參與。單着不是循環的問題了,這里暫時不做討論。很顯然,引入DiscountCalculator打破了Customer和Bill類之間依賴。但是,它能打破所有的循環依賴嗎,包括可能存在與模塊之間的?

image

圖1.2 打破循環   

代碼清單1.3展示了修改后的Customer類,它實現了DiscountCalculator接口,改接口如程序清單4.4所示。

package com.scott.cust;

import java.util.*;
import java.math.BigDecimal;
import com.scott.bill.*;

public class Customer implements DiscountCalculator {
   private List<Bill> bills;
   //特定Customer的折扣根據訂單數目計算
   public BigDecimal getDiscountAmount() {
       if (bills.size() > 5) {
           return new BigDecimal(0.1);
       } else {
           return new BigDecimal(0.03);
       }
   }
   
   public List<Bill> getBills() {
       return this.bills;
   }

   public void createBill() {
       Bill bill = new Bill(this);
       if (bills == null) {
           bills = new ArrayList<Bill>();
       }
       bills.add(bill);
   }

}
代碼清單1.3 修改后的Customer
package com.scott.bill;

import java.math.BigDecimal;

public interface DiscountCalculator {
    public BigDecimal getDisCountAmount();
}
代碼清單1.4 DiscountCalculator

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM