什么是依赖,什么是抽象
1.关于依赖和耦合:从小国寡民到和谐社会
在老子的“小国寡民”论中,提出了一种理想的社会状态:邻国相望,鸡犬之声相闻,民至老死,不相往来。这是他老人家的一种社会理想,老死不相往来的人群呈现了一片和谐景象。因为不发生瓜葛,也就无所谓关联,进而无法导致冲突。这是先祖哲学中的至纯哲理,但理想的大同总是和现实的生态有着或多或少的差距,人类社会无法避免联系的发生,所以小国寡民的理想成为一种美丽的梦想,不可实现。同样的道理,映射到软件“社会”中,也就是软件系统结构中,也预示着不同的层次、模块、类型之间也必然存在着或多或少的联系,这种联系不可避免但可管理。正如人类社会虽然无法实现小国寡民,但是理想的状态下我们推崇和谐社会,把人群的联系由复杂变为简单,由曲折变为统一,同样可以使得这种关联很和谐。所以,软件系统的使命也应该朝着和谐社会的目标前进,对于不同的关系处理,使用一套行之有效的哲学,把复杂问题简单化,把僵化问题柔性化,这种哲学或者说方法,在我看来就是:依赖的哲学,也就是本文所要阐释的中心思想。
因为“耦合是不可避免的”,所以首先就从认识依赖和耦合的概念开始,来一步步阐释依赖的哲学思想。
(1)什么是依赖和耦合
依赖,就是关系,代表了软件实体之间的联系。软件的实体可能是模块,可能是层次,也可能是具体的类型,不同的实体直接发生依赖,也就意味着发生了耦合。所以,依赖和耦合在我看来是对一个问题的两种表达,依赖阐释了耦合本质,而耦合量化了依赖程度。因此,对于关系的描述方式,就可以从两个方面的观点来分析。
从依赖的角度而言,可以分类为:
· 无依赖,代表没有发生任何联系,所以二者相互独立,互不影响,没有耦合关系。
· 单向依赖,关系双方的依赖是单向的,代表了影响的方向也是单向的,其中一个实体发生改变,会对另外的实体产生影响,反之则不然,耦合度不高。
· 双向依赖,关系双方的依赖是相互的,影响也是相互的,耦合度较高。
从耦合的角度而言,可以分类为(此处回归到具体的代码级耦合概念,以方便概念的阐释):
· 零耦合,表示两个类没有依赖。
· 具体耦合,如果一个类持有另一个具体类的引用,那么这两个类就发生了具体耦合关系。所以,具体耦合发生在具体类之间的依赖,因此具体类的变更将引起对其关联类的影响。
· 抽象耦合,发生在具体类和抽象类的依赖,其最大的作用就是通过对抽象的依赖,应用面向对象的多态机制,实现了灵活的扩展性和稳定性。
不同的耦合,代表了依赖程度的差别,以“粒度”为概念来分析其耦合的程度。引用中间层来分离耦合,可以使设计更加优雅,架构更加富有柔性,但直接的依赖也存在其市场,过度的设计也并非可取之道。因为,效率与性能同样是设计需要考量的因素,过多的不必要分离会增加调用的次数,造成效率浪费。
后文分析依赖倒置原则的弊端之一正是对此问题的进一步阐述。
(2)耦合是如何产生的
那么,软件实体之间的耦合是如何产生呢?回归每天挥洒的代码片段,其实就是在重复的创造着耦合,并且得益于对这种耦合带来的数据通信。如果将历史的目光回归到软件设计之初,人类以简单的机器语言来实现最简单的逻辑,给一个输入,实现一个输出,可以表达为如图3-1所示的形式。
随着软件世界的革命,业务逻辑的复杂,以上的简单化处理已经不足以实现更复杂的软件产品,当系统内部的复杂度超越人脑可识别的程度时,就需要通过更科学的方法或者方式来梳理,如图3-2所示。
因此,人类开始发挥重组和简单化处理的优势,开发者不得不在软件设计上做出平衡。平衡的结果就是通过对复杂的系统模块化,把复杂问题简单处理,从而达到能够被人脑识别的目的。基于这种指导原则,随着复杂度的增加模块的划分更加朝着精细化发展,尤其是面向对象程序设计理论的出现,使得对复杂的处理实现了更科学的理论基础。然而,复杂的问题可以通过划分实现简单的功能模块或者技术单元,但由此应运而生的子单元会越来越多,而且越来越多的子单元必须发生数据的通信才能完成统一的业务处理,所以产生的数据通信管理也越来越多。对于子单元的管理,也就是本文关注的核心概念——依赖,成为新的软件设计问题,那么总结前人的经验,提炼今人的智慧,对耦合的产生做如下归纳:
· 继承
· 聚合
· 接口
· 方法调用和引用
· 服务调用
了解了耦合发生的一般方式,就可以进入核心思想的讨论,那就是在认识和了解依赖的基础上,最终追求的目标。
话说
|
设计的目标:高内聚(High cohesion)、低耦合(Low coupling)。
|
讨论了半天,终于是时候对依赖和耦合进行一点儿总结了,也是该进行一点目标诉求了。在软件设计领域,有那么几个至高原则值得每个开发者铭记于心,它们是:
· 面向抽象编程
· 低耦合,高内聚
· 封装变化
· 实现重用:代码重用、算法重用
对了,就是这些平凡的字眼,汇集了面向对象思想的核心内容,也是本文力求阐释的禅意心经。关于面向抽象编程和封装变化,会在后面详细阐释,在此我们需要将注意力关注于“低耦合,高内聚”这一目标。
低耦合,代表了实现最简单的依赖关系,尽可能地减少类与类、模块与模块、层次与层次、系统与系统之间的联系。低耦合,体现了人类追求简单操作的理想状态,按照软件开发的基本实现技巧来追求软件实体之间的关系简单化,正是大部分设计模式力图追求的目标;低耦合,降低了一个类或一个模块发生修改对其他类或模块造成的影响,将影响范围简单化。在本文阐释的依赖关系方式中,实现单向的依赖,实现抽象的耦合,都是实现低耦合的基础条件。
高内聚,一方面代表了职责的统一管理,一方面体现了关系的有效隔离。例如单一职责原则其实归根结底是对功能性的一种指导性体现,将功能紧密联系的职责封装为一个类(或模块),而判断的准则正是基于引起类变化的原因。所以,封装离不开依赖,而抽象离不开变化,二者的概念和本质都是相对而言的。因此,高内聚的目标体现了以隔离为目标进行统一管理的思想。
那么,为了达到低耦合、高内聚的目标,通常意义上的设计原则和设计模式其实都是朝着这个方向实现的,因此仅仅总结并非普遍意义的规则:
· 尽可能实现单项依赖。
· 不需要进行数据交换的双方,不要实现多此一举的关联,人们将此形象称为“不要向陌生人说话(Don't talk to strangers)”。
· 保持内部的封装性,关联的双方不要深入实现细节进行通信,这是保证高内聚的必需条件。
本文节选自《你必须知道的.NET(第2版)》一书
图书详细信息:http://blog.csdn.net/broadview2006/article/details/6673353
分享到:
相关推荐
主要介绍了Spring使用@Autowired为抽象父类注入依赖代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
1 抽象类&&接口,是什么 2 依赖抽象,为什么用 3 二者的区别与选择 代码实现比较。
也就是说,客户只依赖于抽象工厂和抽象产品了。在初始化的时候会用到一次具体工厂类名,我们根据.NET特有的反射机制又可以把这个在客户端唯一的具体的非抽象类放到一个应用程序配置文件中,防止它变化。 这就符合了...
抽象工厂的目的是要提供一个创建一系列相关或相互依赖对象的接口,而不需要指定它们具体的类。这种模式可以汽车制造厂所使用的金属冲压设备中找到。这种冲压设备可以制造汽车车身部件。同样的机械用于冲压不同的车型...
抽象工厂模式的意图是为了提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。这里我写了一些c++的抽象工厂的方法,比较清楚的描写了抽象工厂的实现过程。
提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。 通过主板和cpu组装电脑。这里主板的CPU插槽数要和CPU的针脚数相等,也就是说CPU对象和主板对象是有关联的,同时CPU种类和主板的种类也有很多...
Qt实现的抽象工厂模式,抽象工厂,提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。对于工厂方法来说,抽象工厂可实现一系列产品的生产,抽象工厂更注重产品的组合。
依赖注入依赖注入的抽象层。
在高度依赖计算机的现代社会,软件(特别是大型实时安全攸关软件)的可靠性成为计算机界和整个社会都非常关注的问题。现有的形式化软件验证工具都不得不通过近似来处理复杂问题中的计算,P.Cousot和R.Cousot提出的...
依赖倒置原则的2个重要方针 1.高层模块不应该依赖于低层模块,二者都应该依赖于抽象 2.抽象不应该依赖于细节,细节应该依赖于抽象
修改为高层A依赖于抽象层C,抽象层C是属于A层的,即由A层来规定抽象层C的接口规范,低层B也依赖于抽象层C来具体实现C中的接口,因此通过引入C层,来达到了“倒置”。通过该倒置,引入C层来规范,A和B 可以同时 来...
13.11 模块依赖图表vs数据模型 218 13.12 回顾及讨论 219 13.13 自顶向下的设计 222 13.14 小结 222 练习 223 第14章 从设计到实现 224 14.1 评估一个设计 224 14.2 整理程序开发过程 232 14.3 小结 236 ...
SimpleAST SimpleAST is a Kotlin/Java library designed to parse text into Abstract Syntax Trees. It is heavily inspired by (and began as a port of) Khan Academy's simple-markdown. It strives for ...
分享给大家供大家参考,具体如下: 什么是依赖倒置呢?...因此依赖抽象是实现代码扩展和运行期内绑定(多态)的基础:只要实现了该抽象类的子类,都可以被类的使用都使用。这里,强调一下扩展性这个概念。通
最近有个需求就是一个抽象仓储层接口方法需要SqlServer以及Oracle两种实现方式,为了灵活我在依赖注入的时候把这两种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这...
自定义的一个简单的ioc容器,把创建对象的工作交给配置文件来做。...面向对象编程时面向抽象或者面向借口编程,抽象一般比较稳定,实现抽象的具体肯定是要依赖抽象的,抽象不应该去依赖别的具体,应该依赖抽象。
抽象工厂模式是一种创建型设计模式,它的主要目标是提供一个接口或抽象类,用于创建一系列相关或依赖的对象。该模式的主要优点在于,它可以为客户端代码提供一个统一的接口,使得客户端无需关心具体实现细节。抽象...
AppCore .NET依赖注入 该存储库包含各种依赖项注入容器的抽象和实现。 它针对.NET Framework和.NET Core。 所有工件均已获得。 您可以在开放源代码或商业项目中自由使用它们,只要在重新分发或以其他方式重用我们的...
1.抽象工厂指的是提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类 2.如果多个类有相同的行为,但实际实现不同,则可能需要某种抽象类型作为其父类被继承,抽象类型定义了所有相关具体类将共有的...
它具有:可移植数据库创建,依赖数据库的session支持,SQL执行监控和数据库"健康"检查。 很多的知名网站如ACID, Zikula/PostNuke,Xaraya, phpWiki, Mambo, PHP GACL, TikiWiki, eGroupWare and phpLens都使用了ADODB...