java设计简易记事本软件_java模块化设计:软件复杂性猛如虎!谁才是武松?

本文探讨了软件复杂性随着模块化发展依然存在的问题,指出依赖管理和循环依赖是导致复杂性的主要原因。通过引入抽象接口解决循环依赖,强调了模块化在减少复杂性、提高可维护性和重用性上的重要性。良好的模块和包设计对于软件架构的物理设计至关重要,以适应变化和管理维护成本。
摘要由CSDN通过智能技术生成
4c92cbec4c6affcce2bd4da8bb1f84ea.png

一、复杂性

模块化并不是什么新的理念,早在1972年被人们认识到模块化编程的理念以来,很多事情发生了变化,但是有一点却没有改变,软件依然难以设计和开发。但它的魅力依然让人们趋之若鹜!将设计决策封装在自主的模块中可以使其独立的演化,同时很少暴露内部的实现细节,为其他开发人员屏蔽了不必要的细节。

软件系统在其生命周期开始的时候都是简单的,不复杂的,但是时间会让这一切变得不可收拾!这也符合我们认知,宇宙本身就是一个熵增的过程:一个独立系统,如果在没有外部能量的注入以维持其低熵有序的状态,那么他最终会随时间变得越来越无序,也就是熵增!随着软件系统的演化,它的复杂性会增长,除非你做一些事情对其进行维护或缩减!让我们看一下Spring框架的进化,版本从0.9.1到2.5 这个框架2003年最初发布,代码量增长了六倍!如此大增长如果没有有效维护的手段,后续的扩展和维护将难以维持。

faa06729517237e39920029992d07fc6.png

研究显示,对于一个系统,软件维护以及演化的管理成本会占到总成本的90%以上,这可愁坏了我们这帮写代码的!

74f0c4d1441f784514960b0f4e94ae48.png

软件倾向于随时间而腐化,变化会以悄然并难以预料的方式考验你最初的设计,如果你为了偷懒或赶进度(这是产品的错!)针对每一个细小的变化以一种丑陋甚至暴力的方式进行修改!最终系统也必将会以暴力的方式回报我们!就像财务债一样,技术债务也有利息,在我们曾经因为匆忙和无法选择中而选择了某种杂乱的设计,将来的开发中就要支付额外的利息!

182e953c59325c6d545cf1698236f917.png

软件腐化的最常见原因是依赖大量紧密耦合的代码,当然任何系统要完成对应功能,必须要有依赖。对于大型系统团队来说,有效的分离和管理依赖显得尤为重要,系统的熵会一直增长,除非注入外部能量——维护和解耦!

二、依赖的问题

干扰维护:当我们在一个高度依赖的系统中工作时,会发现应用中一个地方的变化会无形的影响其他很多地方,可能很多时候是无法避免的但经过深思熟虑的依赖结构会试变化更容易一些。

阻止扩展:灵活的软件架构是对修改关闭对扩展开发,通过扩展已有的抽象体将这些扩展插入现有系统,就可以轻松实现软件的扩展!前提是你依赖的是抽象而不是具象,严重依赖性一个很重要的原因是没有正确使用抽象!这让我们扩展新功能的时候感到肝颤!

d9d4591beba300172023a405920a2074.png

抑制重用:具有复杂的包和模块间依赖的软件会极大的降低高级别的重用可能性,一般我们强调的是类级别的重用,但为了实现高级别的重用,对包和模块的结构也要有仔细的考量。

限制测试:类之间的耦合会降低独立测试的能力,可测试性高的系统或模块会给开发人员带来极大的勇气和信心来面对变化,具备很少测试人员的团队不能容易的响应变化,因为他们不能涵盖变化的影响范围。

妨碍集成:随着每个模块组合到一起,会出现一些常见的问题,例如系统性能下降、系统模块的行为粒度不合适、事务性不兼容等问题。过度的依赖会让最终的集成变成一场大爆炸。

d48dab7af95bced79a5cc85121e9b931.png

阻碍理解:复杂依赖结构本质上就难以理解,当问题出现时,你想迅速定位问题,但可能你的思路和这系统一样,剪不断,理还乱。

7b3142dc85bec3617b10919c3ef9b870.png

三、循环依赖

依赖是个魔鬼,但循环依赖就是魔鬼里最厉害的那个!循环可以存在于多种实体间,类、包和模块中。当两个类相互引用时就会产生循环依赖。假设客户类Customer中有一个账单类Bill的列表,而Bill也引用Customer来计算折扣金额,这称为双向关联,那你可能会问,平时不都这样吗?这会有什么问题呢?如果需要测试,这会是一个问题,因为你无法独立测试单个类别,必须引入更多依赖项。

af012e9994ed94867400c4ae67f3fb9f.png

类之间循环依赖

存在循环依赖的代码:

public class Customer {    //这里依赖了账单。。。 省略初始化    private List  bills;    //计算折扣金额    pubilc BigDecimal getDisCountAmount(){    //do something    }}public class Bill {    //这里依赖了客户类。。。省略初始化    private Customer customer;    //计算金额    pubilc BigDecimal pay(){    BigDecimal dis=this.customer.getDisCountAmount();    //使用这个折扣计算最终支付金额 此处省略    }}

有多种方式解决以上问题,其中一种就是引入抽象记住抽象是王道!当然如果单独测试Customer依旧需要Bill的参与,但这已经不是循环依赖了。

58c848f4aa2ee9585d9abcb55ca494f1.png

引入抽象打破循环

消除循环依赖的代码:

public class Customer implements DiscountCalculator {    //账单列表 省略初始化    private List  bills;    //计算折扣金额    @Override    pubilc BigDecimal getDisCountAmount(){    //do something    }}public interface DiscountCalculator {    //计算折扣金额    BigDecimal getDisCountAmount();}public class Bill {    //计算金额    pubilc BigDecimal pay(DiscountCalculator discountCalculator){      //这里不在依赖Customer 而是DiscountCalculator(抽象接口)        BigDecimal dis=discountCalculator.getDisCountAmount();        //使用这个折扣计算最终支付金额 此处省略    }}

当然我们可能并不是有意引入循环依赖,而是在不知不觉引入的,例如如果你把Customer、DiscountCalculator放入一个A模块中(A.jar),而Bill存在于另一个模块B中(B.jar),虽然类级别双向关联消除了,但A和B模块之间依然存在循环依赖。啥意思?有点晕没?来个图先:

e41566e5f2ce1e666753ff0efda0c55a.png

A和B之间存在循环依赖

你从上图中看到了循环依赖了吗?从箭头方向我们可以看到,A和B的关系是循环的。为此我们先引入一条设计原则:【抽象要远离它的实现】,此时可以将DiscountCalculator接口放到B模块中来消除这种循环依赖,当然如果你把抽象体放入单独的新模块中可能会好,这个要根据自己实际需要,不要过度设计就好。

4717e2c431c9731efaed993f55f52847.png

A和B之间没有循环依赖关系

上图,还是看箭头走向,循环消除了!这个例子说明了什么呢?说明了好的类设计(逻辑设计)不一定就是好的架构,好的架构还得看好模块和包的设计(物理设计),java模块化的思考系列——模块的定义一文我提到什么是【物理设计】和【逻辑设计】这个例子充分证明了如果你没有充分考虑物理设计问题,那无论你的逻辑设计有多好都不会带来预期的价值!

还好我们有很多方式来管理循环,测试驱动开发可以让你以优先考虑可测试性、JarAnalyzer放在构建脚本中会生成一个描述JAR文件之间关系的组件图或依赖报告、Maven和类似的工具也有助管理依赖,模块之间循环依赖可以通过的等级化构建来管理,后续我讲详细介绍什么是等级化构建等一系列模式设计原则也就是java模块化的思考系列——模块化包含哪些方面文章提到的设计范式。当然不是所有循环都是不可容忍了,实际开发不可能十全十美,但是无论如何模块之间不能有循环依赖!!!

三、模块的益处

从模块化的定义来看可部署、可管理、可重用、可组合!这些都是重要的益处。但模块化的设计思想消除了多余的依赖来征服开发的复杂性,并管理系统扩展进化的复杂性使我们更容易的理解大型系统所带来的影响。模块化益处不仅仅提供了重用的优势,它更多的它减少了复杂性.

22e371ad5e8ffd471dbc66e752d40451.png

软件系统反馈回路

S代表正比作用

O代表反比作用

结论:所以谁是武松呢?—— 模块化,软件复杂是我们最大的敌人,它会阻止我们以优雅的方式构软件,变化是无法消除的,但模块化会帮助我们适应变化,他会帮助我们征服复杂性。

个人理解有所纰漏,还请老铁们多多指教!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值