1、单一职责
定义
就一个类而言,应该仅有一个引起它变化的原因。
定义解读
这是六大原则中最简单的一种,通俗点说,就是不存在多个原因使得一个类发生变化,也就是一个类只负责一种职责的工作。
优点
- 类的复杂度降低,一个类只负责一个功能,其逻辑要比负责多项功能简单的多;
- 类的可读性增强,阅读起来轻松;
- 可维护性强,一个易读、简单的类自然也容易维护;
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
2、里氏替换
定义
里氏替换原则的定义有两种,据说是由麻省理工的一位姓里的女士所提出,因此以其名进行命名。
- 定义1:如果对一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1所定义的程序P中在o1全都替换成o2时,程序的行为不发生任何变化,那么T2为T1的子类。
- 定义2:所有引用父类的地方都必须能够透明地使用其子类对象。
定义解读
其实两个定义所表达的意思都相同,就是在所有父类出现的地方,子类都可以出现,并且将父类对象替换为子类对象的时候,程序不会抛出任何异常或者错误,因此我们需要注意的是,尽量不要重载或者重写父类的方法(抽象方法除外),因为这样可能会改变父类原有的行为。
优点
- 代码共享,减少创建类的工作量,每个子类都拥有父类的所有属性和方法;
- 提高代码的可重用性;
- 提高代码的可扩张性;
- 提高产品或项目的开放性。
缺点
- 继承是入侵性的,拥有父类的属性和方法;
- 降低代码的灵活性,必须拥有父类的属性和方法;
- 增强耦合性,父类属性或方法改变,需要考虑子类。
3、迪米特法则
定义
- 狭义的迪米特法则定义:也叫最少知识原则(LKP,Least Knowledge Principle)。如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
- 广义的迪米特法则定义:一个模块设计得好坏的一个重要的标志就是该模块在多大的程度上将自己的内部数据与实现有关的细节隐藏起来。信息的隐藏非常重要的原因在于,它可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用阅读以及修改。
定义解读
迪米特法则通常被表述为:Only talk to your immediate friends (只和离你最近的朋友进行交互)。什么是最近的朋友?我们知道,每个对象都会与其他对象之间有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,如依赖、关联、组合、聚合等。其中,我们称出现在成员变量、方法参数、方法返回值中的类为最近的朋友,而出现在局部变量中的类则不是最近的朋友。迪米特法则是很好的解耦合手段之一。在网上看到的比较形象的说明这个法则的示例:
- 如果你想让你的狗狗跑的话,你会对狗狗说还是对四条狗腿说?
- 如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿?
优点
迪米特法则使对象之间的耦合降到最小,符合高内聚低耦合的特性,从而使得类具有很好的可读性和可维护性。
4、依赖倒置原则
定义
高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖细节;细节应该依赖抽象。
定义解读
依赖倒置原则在程序编码中经常运用,其核心思想就是面向接口编程,高层模块不应该依赖低层模块(原子操作的模块),两者都应该依赖于抽象。我们平时常说的“针对接口编程,不要针对实现编程”就是依赖倒转原则的最好体现:接口(也可以是抽象类)就是一种抽象,只要不修改接口声明,大家可以放心大胆调用,至于接口的内部实现则无需关心,可以随便重构。这里,接口就是抽象,而接口的实现就是细节。
如果不管高层模块还是底层模块,它们都依赖于抽象,具体一点就是接口或者抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。
依赖倒转原则其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计(说这句话可能不怎么好理解,再加上一句话就好理解了:面向对象的设计,出发点就是应对变化的问题)。
再举一个生活中的例子,电脑中内存或者显卡插槽,其实是一种接口,而这就是抽象;只要符合这个接口的要求,无论是用金士顿的内存,还是其它的内存,无论是4G的,还是8G的,都可以很方便、轻松的插到电脑上使用。而这些内存条就是具体实现,就是细节。
优点
- 依赖倒置原则可以降低类间的耦合性。
- 依赖倒置原则可以提高系统的稳定性。
- 依赖倒置原则可以减少并行开发引起的风险。
- 依赖倒置原则可以提高代码的可读性和可维护性
依赖倒置原则的实现方法
依赖倒置原则的目的是通过要面向接口的编程来降低类间的耦合性,所以我们在实际编程中只要遵循以下几点,就能在项目中满足这个规则。
- 每个类尽量提供接口或抽象类,或者两者都具备。
- 变量的声明类型尽量是接口或者是抽象类。
- 任何类都不应该从具体类派生。
- 尽量不要覆写基类的方法
- 使用继承时结合里氏替换原则。
5、接口隔离
定义
客户端不应该依赖它不需要的接口;
一个类对另一个类的依赖应该建立在最小的接口上。
定义包含三层含义
- 一个类对另一个类的依赖应该建立在最小的接口上
- 一个接口代表一个角色,不应该将不同的角色都交给一个接口,因为这样可能会形成一个臃肿的大接口
- 不应该强迫客户依赖他们从来不用的方法
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
- 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
- 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
优点
- 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
- 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
- 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
- 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
- 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。
6、开放-关闭原则
定义
一个软件实体(如类 模块 函数)应当对扩展开放,对修改关闭
定义解读
在项目开发的时候,都不能指望需求是确定不变化的,大部分情况下,需求是变化的。那么如何应对需求变化的情况?这就是开放-关闭原则要谈的。
开放-封闭原则的思想就是设计的时候,尽量让设计的类做好后就不再修改,如果有新的需求,通过新加类的方式来满足,而不去修改现有的类(代码)。那么在实际的项目开发中,是否能做到绝对的对修改关闭呢?答案一般也是否定的。既然这样,那么就要求我们在开发前,去找出变化点,然后针对变化点构造抽象,隔离出这些变化。由此可见,实现开闭原则关键是抽象。
优点
- 具有灵活性,通过拓展一个功能模块即可实现功能的扩充,不需修改内部代码。
- 具有稳定性,表现在基本功能类不允许被修改,使得被破坏的程度大大下降。
总结
对于设计模式的六大设计原则,单一职责原则主要说明类的职责要单一;里氏替换原则强调不要破坏继承体系;依赖倒置原则描述要面向接口编程;接口隔离原则讲解设计接口的时候要精简;迪米特法则告诉我们要降低耦合;开闭原则讲述的是对扩展开放,对修改关闭。
六大设计原则并没有很明显的界限,当我们在遵守某一个设计原则的时候,可能也遵守了其他的设计原则。
网友评论