一文get到SOLID原则的重点

 最近没事再次翻开《敏捷软件开发:原则、模式与实践》看,发现以前似懂非懂的东西突然就看懂了,get到了讲的重点。

 SOLID(单一职责原则、开放—封闭原则、里氏替换原则、接口隔离原则以及依赖倒置原则)是由罗伯特·C·马丁引入,成为了面向对象设计中的五个基本原则。当这些原则被一起应用时,它们使得一个程序员开发一个容易进行软件维护和扩展的系统变成可能。 

1 单一职责原则(SRP) 

定义:一个类应该只有一个发生变化的原因

为何把这两个职责分离到分离到单独的类中很重要呢?因为每一个职责都是变化的一个轴线。当需求变化时,该变化会反映为类的职责变化。如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个。如果一个类承担的职责过多,就等于把职责耦合在了一起。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意向不到的破坏。

在SRP中,我们把职责定义为变化的原因,如果你能够想到多于一个的动机去改变一个类,这个类就具有多于一个的职责。有时,我们很难注意到这一点。我们习惯于以组的形式去考虑职责。

仅当变化发生时,变化的轴线才具有实际意义。如果没有征兆,那么应用SRP或者任何其他原则都是不明智的。

软件设计真正要做的许多工作,就是发现职责并把那些职责相互分离。其余原则都会以这样或者那样的方式回到这个问题上。

2 开放封闭原则(OCP) 

定义:软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改。

如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味。OCP建议我们应该对系统进行重构,这样以后对系统再进行那样的改动时,就不会导致更多的修改。如果正确地应用OCP,那么以后进行同样的改动时,就只需要添加新的代码,而不必改动已经正常运行的代码。

怎样可能在不改动模块源代码的情况下去更改它的行为呢?如果不更改一个模块,又怎么能够去更改它的功能呢?答案就是抽象。

策略模式(STARTEGY)和模板方法(TEMPLATE METHOD)模式是满足OCP最常用的方法。应用它们,可以把一个功能的通用部分和实现细节部分清晰的分离开来。

在许多方面,OCP都是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处:灵活性、可重用性、以及可维护性。开发人员应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。

 

3 里氏替换原则(LSP) 

定义:子类型必须能够替换掉它们的基类型。

LSP得出一个非常重要的结论:一个模型如果孤立的看,并不具有真正意义的有效性。模型的有效性只能通过它的客户程序来表现。

LSP基于契约的设计(Design By Contract,DBC)。使用DBC类的编写者显示地规定针对该类的契约。客户代码的编写者可以通过该契约获悉可以依赖的行为方式。契约是通过为每个方法声明前置条件和后置条件来指定的。要使一个方法得以执行,前置条件要为真。执行完毕后,该方法的后置条件为真。派生类的行为方式和输出不能违反基类已经确立的任务限制,基类的用户不应该被派生类的输出扰乱。

OCP是OOD(面向对象设计)中很多说法的核心。如果这个原则应用得有效,应用程序就会具有更强的可维护性、可重用性以及健壮性。LSP是使OCP成为可能的主要原则之一。正是子类型的可替换性才使得使用基类型表示的模块在无需修改的情况下可以扩展。这种可替换性必须是开发人员可以隐式依赖的。这样如果没有在代码中显式地支持基类型的契约,那么就必须很好地、广泛地理解这些契约。子类型正确的定义是可替换的,可替换性通过隐式或者显式的契约来定义。

4 接口隔离原则(ISP) 

定义:不应该强迫客户程序依赖并未使用的方法。

这个原则用来处理“胖”接口所存在的缺点。如果类的接口不是内聚(内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量)的,就表示该类具有“胖”接口。

如果强迫客户程序依赖那些不使用的方法,那么这些客户程序就面临着由于这些未使用的方法的改变所带来的变更。这无意中导致了所有客户程序之间的耦合。换言之,如果一个客户程序依赖于一个含有它不使用的方法的类,但是其他客户却确实要用这个方法,那么当其实客户要求这个类改变时,就会影响到这个客户程序。分离客户就是分离接口。

客户程序应该仅仅以来于它们实际调用的方法。通过把胖类接口分解为多个特定于客户程序的接口,可以实现这个目标。每个特定于客户程序的接口仅仅声明它的特定客户或者客户组调用的那些函数。接着,该胖类就可以继承所有特定于客户程序的接口,并实现它们,这就解除了客户程序和没有调用的方法间的依赖关系,并使客户程序之间互不依赖。

 

5 依赖倒置原则(DIP)

定义:

a.高层模块不应该依赖于低层模块。二者都应该依赖于抽象。

b.抽象不应该依赖于细节。细节应该依赖于抽象。

该原则是框架设计的核心原则。

倒置的含义

为什么使用”倒置”,倒置是较于传统的软件开发,比如结构化分析和设计,总是倾向于创建一些高层模块依赖于低层模块、策略依赖于细节的软件结构。一个设计良好的面向对象的程序,其依赖程序结构相对于传统的过程式方法设计的通常结构而言就是被“倒置”了。

倒置的是什么?

DIP不仅仅是依赖关系的倒置,它也是接口所有权的倒置。客户拥有接口所有权,而它们的服务者则从这些接口派生。

由客户模块或者层来声明它们所需要的服务接口,那么仅当客户需要时才会对接口改变。这样改变实现抽象接口的类就不会影响到客户。

层次化:

所有结构良好的面向对象架构都具有清晰的层次定义,每个层次通过定义一个良好的、受控的接口向外提供了一组内聚的服务。

面象对象的程序设计倒置了依赖关系,使得细节和策略依赖于抽象,并且常常是客户拥有服务接口。依赖关系的倒置正是好的面向对象设计的标志所在。是实现许多面向对象技术所宣称的好处的基本低层机制。它的正确应用对于创建重用的框架来说是必需的。同时它对于构建在变化面前富有弹性也是重要的。由于抽象和细节彼此隔离,所以代码也非常容易维护。

相关阅读

SOLDI原则之DIP:依赖倒置原则

点赞