<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    ??榛睦秩?/span>

    原文链接??作者:Kirk Knoernschild

    ? ? ? ? ???榧涞慕赳詈鲜且恢趾茉愀獾纳杓?,而耦合的最坏表现就是??榧涞难芬览?。幸运的是,有几种方法可以用来消除循环依赖,分别是回调函数,代码上移,代码下移。 接下来,我会为大家展示一个小例子。示例中,我会分别使用上述几种技术来消除循环依赖。

    ? ? ? ? 在消除循环依赖之后,我们会探索另外两项技术,来达到依赖反转和消除??橹湟览档哪康?。本示例的所有代码都可以在Google Code下载,每个解决方法对应的代码都包含有一个编译脚本和一个简单测试用例。一般你只需要输入 ant compile就可以执行编译脚本,不过如果你想使用JarAnalyzer,那么必须要有GraphViz。注意每个解决方法对应的代码版本都具有相同的行为!

    ? ? ? ? ?接下来的例子非常简单。假设有这样一个系统,包含有CustomerBill两个类,分别在两个??橹?/span>—cust.jarbill.jar。同时有一个测试类PaymentTest测试两者交互的行为,集成在billtest.jar???。最初的类图显示在下面,需要引起注意图中两个类的双向关联。

    Class Diagram

    类结构

    ? ? ? ? ? 随着逐步的深入,我们会往系统中添加更多的类和抽象来提高??榛潭?。另外,我们会使用JarAnalyzer来描述??榧涞墓叵?,同时也用来衡量设计的好坏。用JarAnalyzer生成的??榻峁谷缦峦妓?,可以通过查看编译脚本来了解JarAnalyzer的用法?;氐秸?,这篇文章中我们的目的是用上述三种方法来消除循环依赖,再之后会探索不同的途径来构建非循环依赖的???。

    有循环依赖的??榻峁?/p>

    上移

    ? ? ? ? 首先使用的方法叫上移。我们通过将导致依赖的因子(这里是折扣的计算)上移到一个更高级别的???,来达到消除循环依赖的目的。然而在此之前,我们要先弄清楚这个例子中为什么会存在循环依赖。具体原因如下:

    一个Customer拥有多个bill实例,当bill对象的pay方法被调用的时候,需要先去判断是否有折扣。但是,计算折扣的方法是在Customer类中,而不是Bill。因此,Bill类需要调用Customer的方法来计算合适的折扣??梢哉庋此伎颊飧鑫侍?/span>...Customer代表一个付款人,而我们和每个付款人协商折扣。所以折扣的计算是封装在Customer中的。

    ? ? ? ?为了打破这个依赖,我们将导致依赖的因素上移到一个更高级别的类-CustomerMediator。Mediator类将计算折扣的细节封装起来,并传给Bill类。了解这次修改的最好方法就是看修改后的PaymentTest类。我已经修改好了编译脚本,并且将Mediator打包成jar。但如果在深入了解这个类结构后,你可能会质疑为什么不直接把折扣数从Customer类传给Bill类。不用担心,这个例子只是初步的设计,并不是解决此类问题的最好方法。我们要知道的就是,这个方法的核心是将依赖上移到mediator???,来达到消除循环依赖的目的。

    Escalating the Cause of the Cyclic Dependency

    上移循环依赖

    下移

    ? ? ? ? ? 一个解决此类特定循环依赖问题的更好方法是下移(这里CustomerBill间有组合关系)。通过下移,我们将导致依赖的因子下移到一个更低级别的???,这正好和上移 相反。我们引入一个DiscountCalculator类,用来传递给Bill类,修改后的PaymentTest类会生成DiscountCalculator对象并将其传进来。因为Customer类知道怎么计算折扣,所以由Customer类提供生成DiscountCalculator的工厂方法。新的类结构图如下所示。

    Class Structure

    类结构


    ? ? ? ? 接下来我们会修改编译脚本将DiscountCalcultor打包生成calc.jar,最终的??榻峁雇既缦滤?。

    Demoting the Cause of the Cyclic Dependency

    下移循环依赖

    ? ? ? ? 通过分析你会发现下移比上移在处理这种循环依赖的问题上显得更为合理,但是关键的区别是什么呢?使用上移我可以单独部署cust.jarbill.jar。而虽然下移更为合理,但是如果要部署cust.jar或者bill.jar,也必须依赖calc.jar??尚械牡慕饩龇桨缸苁腔岷途咛逦侍夤亓?,而理想的解决方案是在整个开发周期中具有灵活扩展性。

    回调

    ? ? ? ? 回调类似于观察者模式,我们将DiscountCalculator类重构为接口,并让Customer实现这个接口。新的类结构图如下所示。

    Class Structure

    类结构

    ? ? ? ? 在这个例子中,回调类似于下移和最初版本的组合,Customer作为DiscountCalculator类型被传给Bill。与下移中DiscountCalculator类被封装在一个单独的??椴煌氖?,现在我们把它放在bill??橹?。需要注意的是,它不能放在cust???,这会引入循环依赖。新的类结构图如下所示,有点像消除循环依赖的最初版本。

    用回调消除循环依赖

    用回调消除循环依赖

    依赖反转

    ? ? ? ? 接下来我们讨论一下??楣叵?。虽然回调看上去是最合理的解决方法,但如果我们想单独部署cust??槎灰览?/span>bill??槟??回调并不能做到这一点,不过通过一些小技巧,custbill??榈囊览倒叵的芄槐环醋?。

    ? ? ? ? 首先,将Bill类重构为接口,接下来为了避免分离包(同一个包中的不同类被打包到不同的??椋?,我将Bill类和Customer类放在同一个包。新的类图如下所示。

    依赖反转后的类结构

    依赖反转后的类结构

    反转后的??榻峁雇既缦滤?/span> 。

    反转后的??榻峁? width=

    反转后的??榻峁?/p>

    消除依赖

    ? ? ? ? 依赖反转满足了我们这样的需求,独立部署cust??槎挥靡览?/span>bill???。不过现在,我想探索基于独立测试??樾枨蟮慕饩龇椒?。在依赖反转之后,我能够独立的测试cust???,但是如果我想同时独立测试(或者部署)两个??槟??为了达到这个目的,需要彻底消除两者之间的关联。
    ? ? ? ?事实证明,在使用了依赖反转(大多是抽象耦合)后,类结构变得非常灵活。我只需要简单地把两个接口-BillDiscountCalculator-分别封装起来,不需要额外的修改。
    我将它们移到一个新的包base,一样地,修改编译脚本将这两个接口打包到base???。至此,我们成功地消除了bill??楹?/span>cust??榈墓亓?,??榻峁谷缦滤?。

    消除??榧涞囊览? width=

    消除??榧涞囊览?/p>

    总结

    ? ? ? ? 从最开始的两个存在循环依赖的???,到最后??橹涿挥腥魏我览档哪?榻峁?,我们取得了很大的进步。???橹涿挥幸览?,就意味着??榭梢远懒⒉馐院筒渴?。如果你关注我的博客,你应该知道我已经写了大量的文章,关于权衡灵活性和复杂性,可用和重用,以及其他架构和设计方面的。我也希望这个小例子能说清楚这些里面的部分概念。

    ? ? ? ? 最后提示,为了更深入地了解这样设计的目的,以及从对象层次上去理解为什么要这样做,我希望你能亲自运行每个工程的编译脚本,并查看在stats目录下的dependencies.html文件。当然,你需要确保JarAnalyzer正确运行,而JarAnalyzer需要依赖GraphViz。如你所见,相比原始的版本,最终的版本在设计质量上有着显著的提升。

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: ??榛睦秩?/a>


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (0)
    1. 暂无评论

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 1ek| ecg| 2wk| kko| 2yu| 2ig| c0o| oii| mui| w1k| qug| 1qq| im1| maw| m0g| wkw| 0qk| 0ec| ec0| gcc| c0a| mac| 0ii| mai| yk1| iqe| s9o| k9g| g0e| s0i| cs8| uui| mcu| w8c| gew| 9wo| wig| 9ca| 9oc| ma7| cga| qs8| m8c| ycs| gq8| oug| 7yy| km7| g7w| a7s| w7i| wag| wso| 8ok| 6sc| sc6| sg6| ysu| kwi| wea|