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

最近在看《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

时间: 2024-10-11 05:50:19

如何避免类之间的循环依赖的相关文章

面向对象 类与类之间的关系 (依赖,组合,聚合,等)

-----------------------------困难是一位严厉的老师,他产生于克服困难的努力中. # ---------------------------------------------------------------------------------# # class Page:# def __init__(self,lst,pagesize):# self.lst =lst# self.pagesize = pagesize## def start(self,a):#

01 spring循环依赖

作者:Mythsman原文:https://blog.mythsman.com/post/5d838c7c2db8a452e9b7082c/转载自:猿天地原文来自:https://mp.weixin.qq.com/s/pIawXWHCGqIHi0INHDgawQ 1 什么是依赖 其实,不分场景地.笼统地说A依赖B其实是不够准确.至少是不够细致的.我们可以简单定义一下什么是依赖. 所谓A依赖B,可以理解为A中某些功能的实现是需要调用B中的其他功能配合实现的.这里也可以拆分为两层含义: A强依赖B.

类之间的依赖关系和组合关系

目录 类之间的依赖关系和组合关系 依赖(关联)关系 组合(聚合)关系 一对多的组合关系 类之间的依赖关系和组合关系 依赖(关联)关系 类之间可以有三种关系: 依赖(关联)关系 组合(聚合)关系 继承(实现)关系 依赖(关联)关系指的是类对象执行某个动作的时候,需要其他类的对象来帮助完成这个操作的情况,其特点为: 将一个类的对象或者类名传到另一个类的方法中使用 此时的关系是最轻的,随时可以更换其他对象 关联关系的示例如下: class Person: def play(self, tools):

【47】java的类之间的关系:泛化、依赖、关联、实现、聚合、组合

java的类之间的关系:泛化.依赖.关联.实现.聚合.组合 泛化: ? 泛化关系(Generalization)也就是继承关系,也称为"is-a-kind-of"关系,泛化关系用于描述父类与子类之间的关系,父类又称作基类或超类,子类又称作派生类.在UML中,泛 化关系用带空心三角形的直线来表示. ? 在代码实现时,使用面向对象的继承机制来实现泛化关系,如在Java语言中使用extends关键字.在C++/C#中使用冒号":"来实现. 泛化对应Java中继承关系,即子

谈谈类之间的关联关系与依赖关系(转载)

(转载自:http://www.cnblogs.com/iyangyuan/archive/2013/06/16/3138463.html) 对于很多刚刚接触UML的童鞋,可能会对类之间的关联与依赖关系不太理解,今天小菜就浅薄的讲一下. 这块的确是有点乱,不过小菜突然找到了一个比较好的切入点,拿出来分享一下. 接触过设计模式的读者,会经常看到这样的场景:在实例化A类的时候,需要B类作为构造方法的参数,这说明A类需要持有一个B类的引用.比如代理模式.装饰模式等,都会这样做.例如Java中的IO流采

类与类之间的关系--泛化,关联,依赖,实现

类,对象是面向对象的基础,类与类之间的关系是面向对象不可或缺的一部分.下面将从类的关系定义,UML中的符号表示,代码实现三方面介绍类与类之间的关系. 1.泛化(Generalization):也成为继承关系.指一个类(子类或者子接口)继承另外一个类(父类或者父接口)的功能.并可以增加自己的功能.在程序程序中用关键字xtends明确标识,在UML设计中用空三角和实线表示,从子类指向父类,或者子接口指向父接口. 如图              代码: public class Cat { publi

(转)类和类之间的依赖、关联、聚合、组合关系

原文 这一篇讲的挺到位的 一.继承关系     继承指的是一个类(称为子类.子接口)继承另外的一个类(称为父类.父接口)的功能,并可以增加它自己的新功能的能力.在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性.在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口. <ignore_js_op> 二.实现关系     实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系.在Ja

关于声明、定义、前向声明、include、循环依赖、普通友元函数、友元类、友元成员函数的总结

做<C++ Primer>(第5版)253页练习题7.3.4有感,故总结之 1 声明 1.1 变量和函数的声明 常见的声明是声明一个变量或函数,一般在头文件.h中声明,例如: pos cursor = 0; // 给定初始值 char get(pos r, pos col) const; 1.2 类的声明 对于一个类,一般是直接在头文件中直接写 class ClassName { ... },这称之为类的定义,然后在类体{...}中又声明或定义了成员变量和成员函数.类的声明是没有类体,只有个类

Spring之循环依赖

转:http://my.oschina.net/tryUcatchUfinallyU/blog/287936 概述 如何检测循环依赖 循环依赖如何解决 Spring如何解决循环依赖 主要的几个缓存 主要步骤 Spring不能完全解决的循环依赖问题 面对Spring不能完全解决的现状,我们该如何处理 概述 入职的时候学习spring研究过循环依赖,现在再回顾下,发现啥都忘记了,还是得总结下来,故总结该文. 本文主要解决如下问题: 1.何为循环依赖 2.如何检测循环依赖 3.循环依赖可以如何解决 4