在面向对象的设计中,如何通过**很少的设计改变**就可以应对设计需求变化,这是设计者极为要关注的问题。为此,我们需要掌握面向对象设计的**5**大设计原则。 面向对象设计的5大设计原则分别是: 1. 单一职责原则 2. 接口隔离原则 3. 开放-封闭原则 4. 里氏替换原则 5. 依赖倒置原则 这5大原则也是23种类设计模式的基础 ## 单一职责原则 > 单一职责原则(Single Responsibility Principle,SRP) > 一个类被改变的原因不能超过一个,也就是说,一个类只有一个职责,如果职责过多,代码就会臃肿,可读性更差,也更难以维护。 #### 为什么要遵守单一职责原则 1、**提高类的可维护性和可读写性** 一个类的职责少了,复杂度降低了,代码就少了,可读性也就好了,可维护性自然就高了 **2、提高系统的可维护性** 系统是由类组成的,每个类的可维护性高,相对来讲整个系统的可维护性就高。当然,前提是系统的架构没有问题。 **3、降低变更的风险** 一个类的职责越多,变更的可能性就更大,变更带来的风险也就越大 #### 如何遵守单一职责原则 合理的职责分解相同的职责放到一起,不同的职责分解到不同的接口和实现中去 #### 单一职责原则最佳实践 > 在实际工作中,有一个经常会用到的设计模式,DAO模式,又叫数据访问对象,里面定义了数据库中表的增、删、改、查操作. > 按照单一职责原则,为什么不把增、删、改、查分别定义成四种接口?这是因为数据库的表操作,基本上就是这四种类型,不可能变化,所以没有必要分开定义。 > 反而经常变化的是数据库的表结构,表结构一变,这四种操作都要跟着变。所以通常我们会针对一张表实现一个DAO,一张表就代表一种类型的职责。 #### 遵循单一职责原则代码实例 [代码源码](https://github.com/1367636576/phpRookieToMaster/tree/master/PHP核心技术与最佳实战/对象设计原则/单一职责原则) ## 接口隔离原则 接口隔离原则(Interface Segregation Principle,ISP) > 1.客户端不应该依赖它不需要的接口 > 2.类间的依赖关系应该建立在最小的接口上 通俗来讲:使用**多个**专门的接口比使用**单一的总接口**要好。 > 接口如果能够保持粒度够小,就能保证它足够稳定 > 换而言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。 > 过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。 #### 接口隔离原则与单一职责原则对比 **接口隔离原则和单一职责原则非常类似。** > 单一职责原则要求接口的职责是单一的,而接口隔离原则要求接口尽量细化,它们有异曲同工之妙,都是要让我们的接口功能尽量单一,尽量小。 > 但是,单一职责原则的着重点是在“职责”,而接口隔离原则只单纯地要求接口最小化。 > 那么,如果已经满足单一职责原则的接口,在当前的需求下还可以继续细化,那么还需要细化吗?答案是不要再细化了。 > 在实践中,接口设计的粒度越小,系统就越灵活,这是事实。 > 但是灵活的同时也带来了系统的复杂化,导致开发难度增加。 > 所以接口并不是越小越好,必须要有一个度。 > 当单一职责原则和接口隔离原则存在矛盾时,以满足单一职责原则为底线。 ## 开放-封闭原则 开放-封闭原则(Open-Close Principle,OCP) **对于扩展是开放的** > 这意味着模块的行为是可以扩展的。 > 当应用程序的需求改变时,我们可以对其模块进行扩展,使其具有满足那些需求变更的新行为。 > 换句话说,我们可以改变模块的功能。 **对于修改是封闭的** > 对模块行为进行扩展时,不应该影响或大规模地影响已有的程序模块。 > 换句话说,要求开发人员在不修改系统现有的代码(源码或二进制代码)的前提下,实现对系统的扩展。 比如生活中的电脑,满足开放封闭原则。向电脑USB接口插入鼠标,就可以使用鼠标功能,这是开放的(扩展了鼠标功能)。我们不需要对电脑内部部件重新编排,这是封闭的(扩展了鼠标功能,但我们不需要修改电脑配置)。 但这也是相对的,对于一台电脑不可能完全开放,有些设备和功能必须保持稳定才能减少维护上的困难。要实现一项新的功能,你就必须升级硬件,或者换一台更高性能的电脑。 ## 里氏替换原则 里氏替换原则(Liskov Substitution Principle,LSP) **1.子类必须完全实现父类的方法** > 在类中调用其它类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计以及违背了LSP原则 **2.子类可以拥有自己的“个性”** > 子类可以拥有自己的方法和属性, > 也可以重写(Override)或重载(Overload)父类的方法。从而拥有自己的“个性”。 > 但这里提起是为了再次说明一个问题——里氏替换原则可以正着用,但**不能反过来使用**,即子类出现的地方,父类未必可以胜任。 ## 依赖导致原则 依赖导致原则(Dependence Inversion Principle,DIP) > * 高层模块不应该依赖低层模块,两者都应该依赖其抽象; > * 抽象不应该依赖细节; > * 细节应该依赖抽象。 以上3点,通俗的讲 > * 模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的; > * 接口或抽象类不依赖于实现类; > * 实现类依赖接口或抽象类。 简言之就是“**面向接口编程**”