面向对象六大设计原则

概念:

在面向对象设计中,可维护性的复用事宜设计原则为基础的。每一个设计原则都蕴含一些面向对象设计的思想,可以从不同的角度来提升一个软件结构的设计水平。最常见得设计包含7种:

面向对象六大设计原则

单一职责原则

概念:一个类只负责一个功能领域中的相应的职责,或者可以定义为:一个类只有一个引起变化的原因。

单一职责原则是实现高内聚、低耦合的指导方针的,他是最简单又难以运用的原则,需要设计人员发现类的不同职责并将其分离出来,而发现类的多重职需要设计人员有较强的分析能力和丰富的工作经验。

例如:当我们做一个CRM项目的时候,有很多功能需要对数据库中的数据表进行操作,首先需要我们先连接数据库,需要用到的方法是getConnection()的方法,这时我们可以的将这个方法独自创建一个连接数据库的工具类。当需要连接数据库的时候,之间调用这个工具类的方法,而不要每个功能点类独自再去写连接数据库的方法。

开闭原则

概念:开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。其定义是:一个软件实体应当对扩展开放,对修改关闭。即软件实体应当在不修改源代码的情况下进行扩展。软件实体可以是一个软件模块、一个有多个类组成的局部结构或者一个独立的类。

当我们成功上线一个软件之后,随着时间推移,我们必定会出现许多新的功能需求来满足客户需求,这就使得软件维护要求越来越高。在很多面向对象编程语言中提供了很多的接口和抽象类等机制,可以通过定义系统的抽象层,在通过具体的类进行扩展。如果需要对系统进行需改,无修改抽象层,只需添加新的具体类来实现新的业务功能即可,达到开闭原则的要求。

面向对象六大设计原则

我们引入了抽象图表类AbstractChart, 且ChartDisplay针对抽象图表类进行编程, 并通过setChart()方法由客户端来设置实例化的具体图表对象, 在ChartDisplay的display()方法中调用chart对象的display()方法显示图表。 如果需要增加一种新的图表, 如折线图LineChart, 只需要将LineChart也作为AbstractChart的子类, 在客户端向ChartDisplay中注入一个LineChart对象即可, 无须修改现有类库的源代码。

里氏代换原则

概念:所有引用基类(父类)的地方必须能够透明的使用其子类对象。

有Java基础都能够明白父类和子类的使用情况,需要需要使用里氏代换原则需注意以下:

  1. 子类必须实现父类的所有方法,根据里氏代换原则,为例保证系统的可扩展性,在程序中通常使用父类来进行定义
  2. 在运用里氏代换原则,尽量把父类设计为抽象类或者是接口,让子类继承抽象类或者实现父接口,并实现父类中声明的所有方法,运行时,子类实例替换父类实例,我么可以很方便的实现扩展系统功能。里氏代换原则是开闭原则实现的具体手段之一。

实例:正方形 长方形   这里面讲得很清楚,很详细

依赖倒转原则

概念:依赖倒转原则是面向对象设计的主要实现机制之一,他是系统抽象化的具体实现。其定义:抽象不应当依赖于细节,细节应当依赖于抽象,换言之,要针对接口的编程,而不是针对实现编程。

依赖倒转原则要求我们在程序代码中的传递参数时或者关联关系中,尽量引用层高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型转换等,而不要用具体的类。在实现依赖倒转原则时,我们需要针对的是的抽象层编程,而将具体类的对象通过依赖注入到其他对象中,常用的注入方式:

  1. 构造注入:通过构造函数传入具体类的对象
  2. 设置注入(Setter注入):指通过Setter方法来传入具体类的对象
  3. 接口注入:指接口中声明业务方法来出入具体类的对象

接口隔离原则

概念:使用读个单一的接口而不是使用总接口,即客户端不需要依赖那些它不需要的接口。

这里的接口往往有俩种概念:

  • 指一个类型所具有的方法特征集合,仅仅是一种逻辑上的抽象。
  • 某种语言具体的接口定义,有严格的定义和结构,如Java中的interface。

(1) 当把“接口”理解成一个类型所提供的所有方法特征的集合的时候, 这就是一种逻辑上的概念, 接口的划分将直接带来类型划分。 可以把接口理解成角色, 一个接口只能代表一个角色, 每个角色都有它特定的一个接口, 此时, 这个原则可以叫做“角色隔离原则”。
(2) 如果把“接口”理解成狭义的特定语言的接口, 那么ISP表达的意思是指接口仅仅提供客户端需要的行为, 客户端不需要的行为则隐藏起来, 应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。 在面向对象编程语言中, 实现一个接口就需要实现该接口中定义的所有方法, 因此大的总接口使用起来不一定很方便, 为了使接口的职责单一, 需要将大接口中的方法根据其职责不同分别放在不同的小接口中, 以确保每个接口使用起来都较为方便,并都承担某一单一角色。 接口应该尽量细化, 同时接口中的方法应该尽量少, 每个接口中只包含一个客户端( 如子模块或业务逻辑类) 所需的方法即可, 这种机制也为“定制服务”, 即为不同的客户端提供宽窄不同的接口。

合成复用原则(组合/聚合复用原则)

概念:尽量使用对象组合,而不是使用继承来达到复用的目的。合成复用原则就是在一个新的对象里通过关联关系来使用一些已有的对象。

在面向对象设计中,可以同通过俩种方式在不同的环境中复用已有的设计和实现,即同通过组和/聚合关系或者通过继承。但是首先考虑的是合成复用,它更加灵活,降低类与类之间耦合度,其次在考虑继承,使用的继承可以有效降低程序的复杂的,但是滥用继承会增加的系统构建和维护的复杂度。

实例:当一个CustomerDAO类操作MySql数据库的时候,首先需要连接数据库,使用工具类DBUtil提供的getConnection()方法,所以设计成CustomerDAO继承DBUtil,这样CustomerDAO类可以复用DBUtil类的getConnection()方法。假如后期项目的需求更换数据库为Oracle,我们还需要写一个oracle的类来实现连接,这样需要更改CustomerDAO和DBUtil的源码,不符合设计要求。所以这里我们使用合成复用原则,将DBUtil类注入到CustomerDAO类中,将oracle连接类继承DBUtil类,这样我们就不需要修改源码,实现更换数据的方法。

迪米特法则

概念:一个软件实体尽可能少地与其他实体发生相互作用。意思就是当修改一个模块的时候,尽可能的避免其他模块受修改模块的影响。这是对软件实体之间通信的限制,降低模块之间的耦合度,利于系统的维护。

面向对象六大设计原则

就像上图,关系复杂,不便于的维护和修改,对界面新添加一个新的界面控制按钮,会导致的其必须修改源码。所以修改下图的方式:

面向对象六大设计原则

修改成它们共同受一个第三者的类或者接口来控制它们之间的交互问题。

参考书籍《Java设计模式》----刘伟