当前位置:   article > 正文

面向对象设计七大原则_面向对象的七条设计原则

面向对象的七条设计原则

前言

面向对象设计原则是设计模式的基础,每一个设计模式都符合一种或多种面向对象设计原则。它们分别为开闭原则、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则和合成复用原则。
这 7 种设计原则是软件设计模式必须尽量遵循的原则,在实际开发过程中,并不是一定要求所有代码都遵循设计原则,而是要综合考虑人力、时间、成本、质量,不刻意追求完美,要在适当的场景遵循设计原则。这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构,从而有效提高系统的复用性和可维护性。

零、概述

面向对象设计的目标之一在于支持可维护复用,一方面需要实现设计方案或者源代码的重用,另一方面要确保系统能够易于扩展和修改,具有较好的灵活性。由此可见,某些人眼中设计模式只适用于大型程序编写和维护的看法是不对的,设计模式可以让你的代码更具普适性,具有更强的鲁棒性,学习设计模式的过程也会让你对面向对象编程有更深入的了解,同时也能提高你写代码的质量。

最常用的七种面向对象设计原则如表所示,

设计原则名称定义目的使用频率
单一职责原则一个类只负责一个功能领域中的对应职责便于理解,提高代码的可读性5星
开闭原则软件实体应对扩展开放,修改关闭降低维护带来的新风险4星
里氏代换原则所以引用基类(父类)的地方能够透明地使用其子类对象防止继承泛滥5星
依赖倒转原则抽象不应该依赖于细节,细节应该依赖于抽象更利于代码结构的升级扩展5星
接口隔离原则使用多个专门的接口,而不使用单一的总接口功能解耦,高聚合、低耦合2星
合成复用原则尽量使用对象组合,而不是继承来达到复用的目的降低代码耦合4星
迪米特法则软件实体应尽可能少地与其他实体发生相互作用减少代码臃肿3星

PS:星级越高使用频率越高

一、单一职责原则

单一职责原则(Single Responsibility Principle,SRP)又称单一功能原则,由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出的。这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
实例分析
1、实例说明
在某CRM(Customer Relationship Management,客户关系管理)系统中提供了一个客户信息图表显示模块,原始设计方案如图:
在这里插入图片描述
方法功能见方法名,分别为连接数据库,查询所有客户信息,创建、显示图表
2、实例解析
显然CustomerDataChart这个类承担了太多方法的实现,如果其他类也需要连接数据库等操作都会引起这个类的改变,故需要对类进行拆分:
1)DBUtil:负责连接数据库,包含数据库连接方法
2)CustomerDAO:负责操作数据库中的Customer表,包含对Customer表的增删改查
3)CustomerDataChart:负责图表的生成和显示
重构后的图如下:
在这里插入图片描述
从中可见,CustomerDataChart有赖于CustomerDAO提供的客户数据库对象,只需完成查表的单一功能,而CustomerDAO有赖于DBUtil提供的数据库连接服务,这样做就可以在根据后续需求对查询表的类、数据库连接类,获取数据库对象类做出更改,比如查询表类可以派生出其他子类完善展示功能,数据库连接类可以切换SQL型数据库或NoSQL型数据库连接,并且互不干扰,达成有效解耦。

二、开闭原则

开闭原则(Open Closed Principle,OCP)由勃兰特·梅耶(Bertrand Meyer)提出,他在 1988 年的著作《面向对象软件构造》(Object Oriented Software Construction)中提出:软件实体应当对扩展开放,对修改关闭,这就是开闭原则的经典定义
实例分析
1、实例说明
在这里插入图片描述

在某CRM中可以使用不同方式显示图表(饼状图和柱状图等),原始设计方案如图,为了支持多种图表显示方式,在类ChartDisplay的方法display中存在如下代码片段:

if(type.equals("pie")
{
	PieChart chart = new PieChart();
	chart.display();
}else if(type.equals("bar"))
{
	BarChart chart = new BarChart();
	chart.display();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如果新增一个图表类就需要对ChartDisplay类进行改写,违反了开闭原则。
2、实例解析
不难发现图表类展示图表的函数都名为display,只是图表类名不同而导致效果不同,因此何不把图表类抽象出来呢,然后ChartDisplay类只专注于display的调用,而不用关注图表对象的类型。
重构后类图如下:
在这里插入图片描述

三、里氏代换原则

里氏替换原则(Liskov Substitution Principle,LSP)由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出来的,她提出:继承必须确保超类所拥有的性质在子类中仍然成立。
里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
实例分析
1、实例说明
在CRM系统中客户可以分为VIP和普通客户两类,系统需要提供一个发送E-mail的功能,原始设计方案如图,
在这里插入图片描述
2、实例解析
很明显,CommonCustomer和VIPCustomer有很多代码是重复的,假如系统又添加了其他级别的新用户,则需要对EmailSender代码进行修改,因此需要用到继承的方式来改造代码,构造一个Customer基类,让其他客户类作为子类来继承即可简化代码。重构后的类图如下:
在这里插入图片描述

四、依赖倒转原则

依赖倒置原则(Dependence Inversion Principle,DIP)是 Object Mentor 公司总裁罗伯特·马丁(Robert C.Martin)于 1996 年在 C++ Report 上发表的文章。
依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
其核心思想是:要面向接口编程,不要面向实现编程。依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合
实例分析
1、实例说明
在CRM系统中需要将存储在各种文件格式(如TXT文件或Excel文件)中的客户信息转存到数据库中,因此需要进行数据格式转换。在客户数据操作中将调用数据格式转换类的方法实现格式转换和数据库插入操作,原始设计方案如图:
在这里插入图片描述
2、实例解析
这个案例和开闭原则中的案例几乎一模一样,相信学习Spring框架的读者对依赖反转肯定不陌生,Spring框架解决这个问题的时候用的是xml配置文件,说明这是个可行有效的方法。由此给出重构后设计类图:
在这里插入图片描述

五、接口隔离原则

接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。
2002 年罗伯特·C.马丁给“接口隔离原则”的定义是:客户端不应该被迫依赖于它不使用的方法。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上。
以上两个定义的含义是:要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用
实例分析
1、实例说明
在这里插入图片描述

在CRM系统中,设计人员针对客户数据显示模块设计如图接口,其中方法readData用于从文件读取数据,transformToXML方法用于将数据转成XML格式,createChart方法用于创建图表,displayChart方法用于显示,Report两个方法用于创建和显示文字报表。
2、实例解析
以上方案存在致命问题,那就是某个具体数据显示类不需要进行数据转换,但是由于实现接口则需要声明transformToXML方法(至少需要提供一个空实现),可见这对于开发是一种负担累赘。
CustomerDataDisplay这个接口承担了太多方法的定义,有必要分出一些定制好的接口以备使用。重构后类图如下:
在这里插入图片描述

六、合成复用原则

合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。
实例分析
1、实例说明
在初期的CRM系统设计中,由于客户数据不足,系统采用MySQL作为数据库,与数据库操作有关的类如CustomerDAO类等都需要连接数据库,连接数据库的getConnection方法封装在DBUtil类中,由于需要重用DBUtil类的getConnection方法,设计人员将CustomerDAO作为DBUtil的子类,原始设计方案如图:
在这里插入图片描述
2、实例解析
根据合成复用原则,在实现复用时应该多用关联少用继承,重构后类图如下:
在这里插入图片描述
重构后的好处是:只需要在CustomerDAO中注入DBUtil子类对象即可使用该子类的扩展方法,而且可以通过注入不同DBUtil子类很便携地更改DAO的数据库连接方式,原有的代码无须修改,可以更加灵活增加新代码。

七、迪米特法则

迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。
迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性
迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。
实例分析
1、实例说明
在这里插入图片描述

对于CRM系统的客户信息管理界面大致如上图,界面组件之间存在复杂交互关系(见下图),如果删除一个客户,List列表中要删除对应项,底部的统计标签Label也要变化,牵一发而动全身,这在开发中会让人抓狂的。
在这里插入图片描述
2、实例解析
本实例中可以通过引入一共专门用于控制界面组件交互的中间类(Mediator)来降低界面组件间的耦合度。这样界面组件之间不发生直接引用,将请求先转发给中间类,再由中间类来完成对其他组件的调用。当改变组件时只需修改中间类即可。
在这里插入图片描述

总结

能看到这里证明你已经迈出了脱离无脑堆砌代码搬砖生活的重要一步,设计模式在实际生活中常见于大型项目比如框架、应用软件,而中小项目也有所体现。这篇文章是想让读者明白学习设计模式是提高代码编程能力的一种素养和权利,这其中蕴含的面向对象思想,前辈大佬们总结的精华以及成功的开发案例,都是深邃迷人的。
从现实角度,近年来企业开始注重设计模式,在某些公司面试题中多少有体现,设计模式的学习无疑是必经之路。要想走得远就得底子硬,这是最好的时代也是最坏的时代,积极进取才是人生的主旋律。

参考文献

【1】Java设计模式:23种设计模式全面解析
【2】设计模式实训教程(第2版) 刘伟 编著 清华大学出版社

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/403990
推荐阅读
相关标签
  

闽ICP备14008679号