首页 > 代码库 > 如何避免类之间的循环依赖

如何避免类之间的循环依赖

      最近在看《Java应用架构设计 模块化模式与OSGi》,深有感触,在此做些总结。(电子版可以在Java1234.com上下载到)

      在使用Java开发中,各种依赖不可避免。比如类之间的继承,jar包之间的相互依赖。依赖在某种程度上不可避免,但是过多的依赖势必会增加系统的复杂性,使代码难以阅读,从而成为团队开发的阻碍。循环依赖尤其糟糕。

     循环依赖存在与多种实体之间,尤其是类、包、模块之间。当两个类相互引用时,就会出现循环依赖。下面摘抄书中的一个例子。

     如图1.1所示,有Customer和Bill两个类。在本例中,Customer有一个Bill的实例列表,而Bill实例也引用Customer来计算折扣总额。这也成为双向关联(bidirectional association)。对于维护和测试,这将是一个将是一个问题,因为在不引用另一个类的情况下,你不能单独的对其中一个类做任何事情。

  技术分享

图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类之间依赖。但是,它能打破所有的循环依赖吗,包括可能存在与模块之间的?

技术分享

图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

 

如何避免类之间的循环依赖