单一职责原则
Single Responsibility Principle,SRP
定义:一个类只负责一个功能领域中的相应职责。或者定义为:就一个类而言,应该只有一个引起它变化的原因。
简单来说,就是类不能太大,一个类承担的职责越多,它被复用的可能性就越小,当一个类承担的职责过多,就相当于这些职责耦合在一起,当其中一个职责变化时,可能导致其他不必要的职责也发生变化,因此需要将这些职责进行分离,将不同职责的类封装在不同的类中。如果多个职责总是同时发生改变,则可以将它们封装在同一个类中。
具体来看,如何判断一个类是否职责过多呢?就一个类来说,应该只有一个引起它变化的原因。
开闭原则
Open-Closed Principle,OCP
定义:软件实体应对拓展开放,而对修改关闭。即软件实体应尽量在不修改代码的情况下拓展。(新增类,不改代码)
具体做法就是通过接口、抽象类为系统定义一个相对稳定的抽象层,将不同的实现行为移至具体实现层。这样不修改原有代码,也可以添加新的实现行为。
里氏代换原则
Liskov Substitution Principle,LSP
所有引用基类对象的地方能够透明地使用其子类的对象。(而且子类不要违背父类的声明规约)
举例说明:
若满足里氏代换原则
BaseClass a = new DerivedClass()
DerivedClass
中的所有方法对a都应当是可见的。
要做到这一点,则派生类(具体类)中的公开方法不能多于基类(接口或抽象类)声明的方法。否则,引用基类的对象是不可见那个方法的。也即不透明;而且子类的实现必须符合父类的方法定义。
依赖倒转原则
Dependence Inversion Principle,DIP
定义:抽象不应该依赖于细节,细节应该依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
这就要求程序代码中传递参数或者在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明、以及数据类型转换等,而不要用具体类来做这些事情。(当然,声明后的初始化肯定需要具体类,声明抽象是为了方便切换具体类)
在引入抽象层后,要注意使其满足里氏代换原则。即一个具体类应只实现接口或抽象层中声明的方法,而不要给出多余的方法,否则将无法调用子类中增加的新方法。
接口隔离原则
Interface Segregation Principle,ISP
定义:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
如果接口有大量的方法,一方面导致接口的实现类很庞大,在不同实现类中都不得不实现接口中定义的所有方法,灵活性较差,如果出现大量的空方法,导致系统产生大量的无用代码;另一方面,如果客户端针对大接口编程,将在一定程度破坏程序的封装性,客户端看到了不该看到的方法。
在使用接口隔离原则时,需要注意控制接口的粒度,接口太小会使接口泛滥,接口太大会使灵活性差。
合成复用原则
Composite Reuse Principle ,CRP
定义:尽量使用对象组合,而不是继承来达到复用的目的。在一个新的对象里通过关联关系来使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用功能的目的。
通过继承来进行复用会有一些问题,从基类继承而来的实现使静态的,无法在运行时发生改变,即没有足够的灵活性,这种复用又被称为白箱复用。
通过关联将已有对象纳入新对象的一部分,新对象就可以调用已有对象的功能,这样做可以使成员对象的内部实现细节对于新对象不可见,相对于继承关系而言,其耦合度要低。新对象可以动态的引用成员对象类型相同的其他对象,这种复用又被称为黑箱复用。
迪米特法则
Law of Demeter,LOD
一个软件实体应尽可能少地与其他实体发生相互作用。
类与类之间地要尽可能地降低耦合度。一个处于松耦合的类一旦被修改,不会对关联的类造成太大波及。两个对象之间的交互要尽可能的少。