前言
设计模式共有六个原则,分别是:
- 单一职责原则
- 开闭原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
- 迪米特法则
单一职责原则告诉我们实现类要职责单一;
里氏替换原则告诉我们不要破坏继承体系;
依赖倒置原则告诉我们要面向接口编程;
接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合;
开闭原则是告诉我们要对扩展开放,对修改关闭。
对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。
任何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。
下面将分别进行具体介绍。
单一职责原则
定义
单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
特点
一个类只负责一项职责。如果一个类包含多种职责,就应该把类拆分。
适用问题
如果类A有两个职责:d1,d2。当职责d1需要修改时,可能会导致原本运行正常的职责d2功能产生问题。
解决方法
如果一个类包含多种职责,就应该把类拆分。分别建立两个类A、B,让A负责d1,B负责d2。当需要修改某一职责,那么将不会对另外一个功能产生影响。
原则
只有逻辑足够简单,才可以在代码级别上违反单一职责原则;只有类中方法数量足够少,才可以在方法级别上违反单一职责原则
优点
• 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
• 提高类的可读性,提高系统的可维护性;
• 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
开闭原则
定义
开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
特点
对扩展开放,对修改关闭。
适用问题
在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误。
解决方法
当软件需要变化时,不应该通过修改已有的代码来实现变化,而是尽量通过扩展原有代码。
优点
使程序的扩展性好,易于维护和升级。
里氏替换原则
定义
里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。
特点
这一原则与继承紧密相关。如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
适用问题
当使用继承时,遵循里氏替换原则。子类可以扩展父类的功能,但不能改变父类原有的功能。
解决方法
在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。
原则
(1)子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
(2)子类中可以增加自己特有的方法。
(3)当子类的方法重载父类的方法时,方法的形参要比父类方法的输入参数更宽松。
(4)当子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。
优点
提高继承体系的可复用性,大大降低运用多态产生错误的可能性。
依赖倒置原则
定义
依赖倒转原则(Dependency Inversion Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
特点
抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
适用问题
类A的方法依赖类B,如果需要A通过C来实现同样的功能,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方法
通过面向接口编程,将方法的参数类型设置为接口。而之前的具体实现类来实现接口中的方法。
原则
• 低层模块尽量都要有抽象类或接口,或者两者都有。
• 变量的声明类型尽量是抽象类或接口。
• 使用继承时遵循里氏替换原则。
优点
采用依赖倒置原则给多人并行开发带来了极大的便利。参与协作开发的人越多、项目越庞大,采用依赖倒置原则的意义就越重大。现在很流行的TDD开发模式就是依赖倒置原则最成功的应用。
接口隔离原则
定义
接口隔离原则(Interface Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
特点
实现类不应该依赖它不需要实现接口具体方法的接口。
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
适用问题
接口A需要实现方法a、b、c,具体实现类B实现方法a、b,实现类C实现a、c。所以B,C需要实现它们不需要的方法。
解决方法
将接口A继续拆分为独立的几个接口,类B和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
原则
• 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
• 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
• 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情
优点
很多人会觉的接口隔离原则跟之前的单一职责原则很相似,其实不然。其一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。
迪米特法则
定义
迪米特法则(Law of Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用。
特点
一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
适用问题
类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方法
尽量降低类与类之间的耦合。
原则
高内聚,低耦合。
优点
极大地降低程序耦合性。