面向对象设计原则之依赖倒转原则

前两天的算法排序文章,《Java常用排序算法/程序员必须掌握的8大排序算法(上)》《Java常用排序算法/程序员必须掌握的8大排序算法(下)》,没看的可以点上面两个链接↑↑↑,用了概念+实例+代码+排序舞蹈视频更好的帮助大家理解!今天继续发表设计模式系列的文章,没看过的同学可以在公众号界面点击历史文章,可以查看前几天关于设计模式系列文章。

作者:刘伟 来源:http://blog.csdn.net/lovelion/article/details/7562783

面向对象设计原则之依赖倒转原则

如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现。依赖倒转原则是Robert C. Martin在1996年为“C++Reporter”所写的专栏Engineering Notebook的第三篇,后来加入到他在2002年出版的经典著作“Agile Software Development, Principles, Patterns, and Practices”一书中。依赖倒转原则定义如下:

依赖倒转原则(Dependency Inversion Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。

在实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中,依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有三种,分别是:构造注入,设值注入(Setter注入)和接口注入。构造注入是指通过构造函数来传入具体类的对象,设值注入是指通过Setter方法来传入具体类的对象,而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。

扩展

软件工程大师Martin Fowler在其文章Inversion of Control Containers and the Dependency Injection pattern中对依赖注入进行了深入的分析,参考链接:

http://martinfowler.com/articles/injection.html

下面通过一个简单实例来加深对依赖倒转原则的理解:Sunny软件公司开发人 员在开发某CRM系统时发现:该系统经常需要将存储在TXT或Excel文件中的客户信息转存到数据库中,因此需要进行数据格式转换。在客户数据操作类中将调用数据格式转换类的方法实现格式转换和数据库插入操作,初始设计方案结构如图1所示:

图1 初始设计方案结构图 在编码实现图1所示结构时,Sunny软件公司开发人员发现该设计方案存在一个非常严重的问题,由于每次转换数据时数据来源不一定相同,因此需要更换数据转换类,如有时候需要将TXTDataConvertor改为ExcelDataConvertor,此时,需要修改CustomerDAO的源代码,而且在引入并使用新的数据转换类时也不得不修改CustomerDAO的源代码,系统扩展性较差,违反了开闭原则,现需要对该方案进行重构。

在本实例中,由于CustomerDAO针对具体数据转换类编程,因此在增加新的数据转换类或者更换数据转换类时都不得不修改CustomerDAO的源代码。我们可以通过引入抽象数据转换类解决该问题,在引入抽象数据转换类DataConvertor之后,CustomerDAO针对抽象类DataConvertor编程,而将具体数据转换类名存储在配置文件中,符合依赖倒转原则。根据里氏代换原则,程序运行时,具体数据转换类对象将替换DataConvertor类型的对象,程序不会出现任何问题。更换具体数据转换类时无须修改源代码,只需要修改配置文件;如果需要增加新的具体数据转换类,只要将新增数据转换类作为DataConvertor的子类并修改配置文件即可,原有代码无须做任何修改,满足开闭原则。重构后的结构如图2所示:

图2重构后的结构图

在上述重构过程中,我们使用了开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。

扩展

Robert C. Martin(Bob大叔):Object Mentor公司总裁,面向对象设计、模式、UML、敏捷方法学和极限编程领域内的资深顾问。

再上两张Bob大叔的“玉照”,

原文发布于微信公众号 - zhisheng(zhisheng_blog)

原文发表时间:2016-05-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Coding01

Javascript 从异步函数到 Promise 到 Async/Await

我最近正在看的一本书《聊聊架构》,在进入今天的主题之前,我想和大家分享这本书里的一个概念“生命周期”。

1774
来自专栏申龙斌的程序人生

零基础学编程002:Hello World

昨天介绍了codecademy在线学编程的网站,不知道大家动手试验了没有?是不是太简单,一下子就完成了许多练习? 第一课的内容只有一条输出语句,点击保存并提交...

2937
来自专栏JAVA高级架构开发

Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码

美国时间 09 月 25 日,Oralce 正式发布了 Java 11,这是据 Java 8 以后支持的首个长期版本。

1583
来自专栏偏前端工程师的驿站

JS魔法堂:不完全国际化&本地化手册 之 实战篇

前言  最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求——国际化&本地化。熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"...

27110
来自专栏编程

Shell 中的命令替换及参数扩展

今天和大家分享的主要是 “命令替换” 以及 “参数扩展” 。 -- 季文康 本文导航 ◈ 前言 00% ◈ 什么是命令替换 14% ◈ 1、一层 SHELL 嵌...

2089
来自专栏Java技术栈

Java 10的10个新特性,将彻底改变你写代码的方式!

Java 9才发布几个月,很多玩意都没整明白,现在Java 10又要来了。。 这时候我真尼玛想说:线上用的JDK 7 甚至JDK 6,JDK 8 还没用熟,JD...

4408
来自专栏恰童鞋骚年

设计模式的征途—3.工厂方法(Factory Method)模式

上一篇的简单工厂模式虽然简单,但是存在一个很严重的问题:当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代...

762
来自专栏PhpZendo

如何使用 Laravel Collections 类编写神级代码

Laravel 提供了一些超赞的组件,在我看来,它是目前所有 Web 框架中提供组件支持最好的一个。它不仅提供了开箱即用的视图(views)、身份认证(auth...

1272
来自专栏walterlv - 吕毅的博客

应该抛出什么异常?不应该抛出什么异常?(.NET/C#)

2018-02-04 13:25

1862
来自专栏海说

[转]我的编码习惯 - 接口定义

工作中,少不了要定义各种接口,系统集成要定义接口,前后台掉调用也要定义接口。接口定义一定程度上能反应程序员的编程功底。列举一下工作中我发现大家容易出现的问题:

953

扫码关注云+社区

领取腾讯云代金券