工做那么久,才知道的 SOLID 设计原则

认识 SOLID 原则

不管是软件系统设计,仍是代码实现,遵循有效和明确的设计原则,都利于系统软件灵活可靠,安全快速的落地,更重要的是能灵活地应对需求,简化系统扩展和维护,避免无效的加班。本文主要讨论面向对象软件开发中最流行的设计原则- SOLID,它是五个设计原则为了方便记忆而组成的首字母缩写:java

  • 单一职责原则
  • 开放/封闭原则
  • 里式替换原则
  • 接口隔离原则
  • 依赖倒置原则

S:单一职责原则 (SRP)

基本概念

单一职责原则 (SRP) 英文全称为 Single Responsibility Principle,是最简单,但也是最难用好的原则之一。它的定义也很简单:对于一个类而言,应该仅有一个引发它变化的缘由。其中变化的缘由就表示了这个类的职责,它多是某个特定领域的功能,多是某个需求的解决方案。


这个原则表达的是不要让一个类承担过多的责任,一旦有了多个职责,那么它就越容易由于某个职责而被更改,这样的状态是不稳定的,不经意的修改颇有可能影响到这个类的其余功能。所以,咱们须要将不一样的职责封装在不一样的类中,即将不一样的变化缘由封装在不一样的类中,不一样类之间的变化互不影响。设计模式

实例说明

举一个具体的例子,有一个用于实现编辑和打印报表的类,这样的类存在两个变化的缘由:第一,报表的内容能够改变(编辑)。第二,报表的格式能够改变(打印)。若是有一个对于报表编辑流程的修改,而报表的编辑流程会致使公共状态或者依赖关系的改变,使得打印功能的代码没法工做。因此单一职责原则认为这两个变化的缘由事实上是两个分离的功能,它们应该分离在不一样的类中。
image.png
安全

相关设计模式

面对违背单一职责原则的程序代码,咱们能够利用外观模式,代理模式,桥接模式,适配器模式,命令模式对已有设计进行重构,实现多职责的分离。
框架

小结

单一职责原则用于控制类的粒度大小,减小类中不相关功能的代码耦合,使得类更加的健壮;另外,单一职责原则也适用于模块之间解耦,对于模块的功能划分有很大的指导意义。
函数

O:开闭原则 (OCP)

基本概念

开闭原则 (OCP) 英文全称为 Open-Closed Principle,基本定义是软件中的对象(类,模块,函数等)应该对于扩展是开放的,可是对于修改是封闭的。这里的对扩展开放表示这添加新的代码,就可让程序行为扩展来知足需求的变化;对修改封闭表示在扩展程序行为时不要修改已有的代码,进而避免影响原有的功能。


要实现不改代码的状况下,仍要去改变系统行为的关键就是抽象和多态,经过接口或者抽象类定义系统的抽象层,再经过具体类来进行扩展。这样一来,无须对抽象层进行任何改动,只须要增长新的具体类来实现新的业务功能便可,达到开闭原则的要求。spa

实例说明

一样,举个例子来更深入地理解开闭原则:有一个用于图表显示的 Display 类,它能绘制各类类型的图表,好比饼状图,柱状图等;而须要绘制特定图表时,都强依赖了对应类型的图表,Display 类的内部实现以下:设计

public void display(String type) {
    if (type.equals("pie")) {  
      PieChart chart = new PieChart();  
      chart.display();  
    }  else if (type.equals("bar")) {  
      BarChart chart = new BarChart();  
      chart.display();  
    } 
}

基于上述的代码,若是须要新增一个图表,好比折线图 LineChart ,就要修改 Display 类的 display() 方法,增长新增的判断逻辑,很显然这样的作法违反开闭原则。而让类的实现符合开闭原则的方式就是引入抽象图表类 AbstractChart,做为其余图表的基类,让 Display 依赖这个抽象图表类 AbstractChart,而后经过 Display 决定使用哪一种具体的图表类,实现代码变成了这样:3d

private Abstractchart chart;

public void display() {
    chart.display();  
}

如今咱们须要新增折线图显示,在客户端向 Display 中注入一个 LineChart 对象便可,无须修改现有类库的源代码。
image.png
代理

相关设计模式

面对违背开闭原则的程序代码,能够用到的设计模式有不少,好比工厂模式,观察者模式,模板方法模式,策略模式,组合模式,使用相关设计模式的关键点就是识别出最有可能变化和扩展的部分,而后构造抽象来隔离这些变化。
code

小结

有了开闭原则,面向需求的变化就能进行快速的调整实现功能,这大大提升系统的灵活性,可重用性和可维护性,但会增长必定的复杂性。

L: 里式替换原则 (LSP)

基本概念

里式替换原则 (LSP) 英文全称为 Liskov Substitution Principle,基本定义为:在不影响程序正确性的基础上,全部使用基类的地方都能使用其子类的对象来替换。这里提到的基类和子类说的就是具备继承关系的两类对象,当咱们传递一个子类型对象时,须要保证程序不会改变任何原基类的行为和状态,程序能正常运做。

实例说明

为了能理解里式替换原则,这里举一个经典的违反里式替换原则的例子:正方形/长方形问题。
image.png
上图为正方形/长方形问题的类层次结构,Square 类继承了 Rectangle 类,可是 Rectangle 类的宽高能够分别修改,可是 Suqare 类的宽高则必须一同修改。若是 User 类操做 Rectangle 类时,但实际对象是 Suqare 类型时,就会形成程序的出错,以下方代码:

Rectangle r = ...; // 返回具体类型对象
r.setWidth(5);
r.setHeight(2);
assert(r.area() == 10);

当返回具体类型对象为 Suqare 类型,面积为 10 的断言就是失败,这样明显是不符合里式替换原则的。

小结

要让程序代码符合里式替换原则,须要保证子类继承父类时,除添加新的方法完成新增功能外,尽可能不要重写父类的方法,换句话就是子类能够扩展父类的功能,但不能改变父类原有的功能。


另外一方面,里式替换原则也是对开闭原则的补充,不只适用于继承关系,还适用于实现关系的设计,常提到的 IS-A 关系是针对行为方式来讲的,若是两个类的行为方式是不相容,那么就不该该使用继承,更好的方式是提取公共部分的方法来代替继承。

I:接口隔离原则 (ISP)

基本概念

接口隔离原则 (ISP) 英文全称为 Interface Segregation Principle,基本定义:客户端不该该依赖那些它不须要的接口。客户端应该只依赖它实际使用的方法,由于若是一个接口具有了若干个方法,那就意味着它的实现类都要实现全部接口方法,从代码结构上就十分臃肿。

实例说明


image.png


如今咱们看下一个违反接口隔离原则的例子,从上面类结构图中,有多个用户须要操做 Operation 类。若是 User1 只须要使用 operation1 方法,User2 只须要使用 operation2 方法,User3 只须要使用 operation3 方法,那么很明显对于 User1 来讲,不该该看到 operation2 和 operation3 这两个方法,要减小对本身不关心的方法的依赖,防止 Operation 类中 operation2 和 operation3 方法的修改,影响到 User1 的功能。这个问题能够经过将不一样的操做隔离成独立的接口来解决,具体以下图所示。
image.png






基于接口隔离原则,咱们须要作的就是减小定义大而全的接口,类所要实现的接口应该分解成多个接口,而后根据所须要的功能去实现,而且在使用到接口方法的地方,用对应的接口类型去声明,这样能够解除调用方与对象非相关方法的依赖关系。总结一下,接口隔离原则主要功能就是控制接口的粒度大小,防止暴露给客户端无相关的代码和方法,保证了接口的高内聚,下降与客户端的耦合。

D:依赖倒置原则 (DIP)

基本概念

依赖倒置原则 (DIP) 英文全称 Dependency Inversion Principle, DIP),基本定义是:

  • 高层模块不该该依赖低层模块,应该共同依赖抽象;
  • 抽象不该该依赖细节,细节应该依赖抽象。

这里的抽象就是接口和抽象类,而细节就是实现接口或继承抽象类而产生的类。

实例说明

如何理解“高层模块不该该依赖低层模块,应该共同依赖抽象”呢?若是高层模块依赖于低层模块,那么低层模块的改动颇有可能影响到高层模块,从而致使高层模块被迫改动,这样一来让高层模块的重用变得很是困难。
file而最佳的作法就如上图同样,在高层模块构建一个稳定的抽象层,而且只依赖这个抽象层;而由底层模块完成抽象层的实现细节。这样一来,高层类都经过该抽象接口使用下一层,移除了高层对底层实现细节的依赖。

相关设计模式

关于依赖倒置原则,能够用到的设计模式有工厂模式,模板方法模式,策略模式。

小结

依赖倒置原则能够减小类间的耦合性,提升系统的稳定性,下降并行开发引发的风险,提升代码的可读性和可维护性。同时依赖倒置原则也是框架设计的核心原则,善于建立可重用的框架和富有扩展性的代码,好比 Tomcat 容器的 Servlet 规范实现,Spring Ioc 容器实现。

结语

到这里,SOLID 设计原则就所有介绍完了,本文的主要目的仍是对这六项原则系统地整理和总结,在后续的程序设计开发过程当中能有意识地识别出设计原则和模式。若是你们对设计原则有更多想法和理解,欢迎留言,你们共同探讨。

相关文章
相关标签/搜索