S.O.L.I.D是面向对象设计和编程(OOD&OOP)中几个重要编码原则的首字母缩写。这几条原则是非常基础而且重要的面向对象设计原则。正是由于这些原则的基础性,理解、融汇贯通这些原则需要不少的经验和知识的积累。
SRP : 单一责任原则
当需要修改某个类的时候原因有且只有一个。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。
通常一个类被修改的几率很大,因此我们应该专注单一功能。假如我们把很多功能放在一个类中,功能之间就形成了关联,我们修改其中一个功能便有可能中止另一个功能,这时我们就需要测试来避免可能出现的问题,浪费了很多时间。一个好的设计是把不同的职责分离出来,放进不同的类中,这样改变任意一个不会影响到其他的应用程序。
OCP: 开放封闭原则
软件实体应该是可扩展,而不可修改的。也就是说,对扩展开放,对修改关闭。这是另一种非常棒的设计原则,可以防止其他人更改已经测试好的代码。理论上,可以在不修改原有的模块的基础上,扩展功能。这也是开闭原则的宗旨。这个原则是诸多面向对象编程原则中最抽象、最难理解的一个。
1、通过增加代码来扩展功能,而不是修改已经存在的代码。
2、如果客户模块和服务模块遵循同一个接口来设计,那么客户模块可以不关心服务模块的类型,服务模块可以方便扩展服务(代码)。
3、OCP支持替换的服务,而不用修改客户模块。
例如:我们现在有两种发送信息的方式,现在我们要增加一中方式。我们将需要在原来的方法中新增一个方法,在修改调用它的地方,这并不符合OCP原则。我们可以抽象出一个发送的接口,里面有发送的方法,然后我们让之前的两种发送方法去实现它,这样一来我们不管增加几种发送发式,只需要添加它的实现类和实现接口就可以了。不用再修改已有的接口和实现类,很好的遵循了OCP原则。在项目中应用OCP原则可以限制代码的更改,一旦代码完成,测试和调试之后就很少再去更改。这减少了给现有代码引入新bug的风险,增强软件的灵活性。
LSP : 里氏替换原则
当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系。
换句话来说就是子类型能够完全替换父类型,而不会让调用父类型的客户程序从行为上有任何改变。因此,所有的子类必须按照和他们父类相同方式操作。子类的特定功能可能不同,但是必须符合父类的预期行为。一般来说,如果父类型的一个子类型做了一些父类型的客户没有预期的事情,那这就违反LSP。
LSP经典案例是矩形和正方形的案例,正方形是要求长等于宽,如果你应该使用矩形的时候你却使用了正方形,不可预期的错误会发生,因为正方形的尺寸是不能被修改 (修改了就不是正方形,违背事物内在逻辑一致性)。
我们回到面向对象的基本概念上,子类继承父类表达的是一种IS-A关系,IS-A关系这种用法被认为是面向对象分析(OOA)基本技术之一。
《Java与模式》一书中的解释是:我们设计继承体系时,子类应该是可替代的父类的,是可替代关系,而不仅仅是IS-A的关系。
而PPP一书中的解释是:从行为方式的角度来看,正方形不是长方形,对象的行为方式才是软件真正所关注的问题 ,LSP清楚地指出,OOD(面向对象设计)中IS-A关系时就行为方式而言的,客户程序是可以对行为方式进行合理假设的。
其实二者表达的是同一个意思。
ISP : 接口分离原则
不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
当你应用ISP时,类和他们的依赖使用紧密集中的接口通信,最大限度地减少了对未使用成员的依赖,并相应地降低耦合度。小接口更容易实现,提升了灵活性和重用的可能性。由于很少的类共享这些接口,为响应接口的变化而需要变化的类数量降低,增加了鲁棒性。
不要依赖你不需要的东西:
示例1:一个动物的行为接口中包含了“吃”、“爬”和“跑”,但蛇是不会跑的,所以有些是不必须的方法,更好的方法是我们将“爬”和“跑”单独做一个接口。这个要根据实际情况做调整,如果不是每个继承该接口的类都需要,就不能将所有的功能都放在同一个接口里。
示例2:一个提款机有很多不同的操作,我们在取钱(拿走)的时候没服务费,在取钱(转账)的时候要扣服务费,我们能在取钱(拿走)的时候,提示取款扣服务费吗?
为什么要ISP(接口分离原则):
否则 - 增加了不同客户端之间的耦合
每个客户端对SRP基本上是一个变量
DIP: 依赖倒置原则
1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
2. 抽象不应该依赖于细节,细节应该依赖于抽象
DIP是定位在高层次模块不应该依赖低层次模块,它们应该只依赖于抽象,这就能帮助我们实现松耦合,使得设计易于修改,DIP允许测试 数据库细节能够如插件一样插入我们的系统。
1、高层模块不要依赖低层模块
2、高层和低层模块都要依赖于抽象
3、抽象不要依赖于具体实现
4、具体实现要依赖于抽象
5、抽象和接口使模块之间的依赖分离。
那么具体怎么优化依赖关系才能让模块或层间的耦合更低呢?想想前面讲的OCP原则吧,观点是类似的。
总结:
这几条原则是非常基础而且重要的面向对象设计原则。正是由于这些原则的基础性,理解、融汇贯通这些原则需要不少的经验和知识的积累。
1、一个对象只承担一种责任,所有服务接口只通过它来执行这种任务。
2、程序实体,比如类和对象,向扩展行为开放,向修改行为关闭。
3、子类应该可以用来替代它所继承的类。
4、一个类对另一个类的依赖应该限制在最小化的接口上。
5、依赖抽象层(接口),而不是具体类。
本文部分来源:http://blog.csdn.net/dxpqxb/article/details/51732555
备注:
层(layer)体系架构模式就是把应用系统分解成子任务组,其中每个子任务组处于一个特定的抽象层次上。
IOC(控制反转):全称为:Inverse of Control。从字面上理解就是控制反转了,将对在自身对象中的一个内置对象的控制反转,反转后不再由自己本身的对象进行控制这个内置对象的创建,而是由第三方系统去控制这个内置对象的创建。
DI(依赖注入):全称为Dependency Injection,意思自身对象中的内置对象是通过注入的方式进行创建。
IOC就是一种软件设计思想,DI是这种软件设计思想的一个实现。而Spring中的核心机制就是DI。