1、单一职责原则
定义:不能存在多个导致类变更的原因;既:一个类只负责一个职责。
问题的原因:一个类class1负责两个不同的职责:P1,P2,如需职责P1需要变更类class1时,有可能导致可以正常运行的职责P2功能出现问题。
解决方案:遵循单一职责原则,一个类负责一个职责功能,要更改该职责是不会影响其他类的职责功能。
2、里氏替换原则
定义:所有引用基类的地方必须能透明的使用其子类的对象。
或:
子类对象可以在程序中替换父类对象;
或:
如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
问题原因:功能P1由类A完成。需要对功能P1进行扩展,扩展后的功能为P,其中P由原来功能P1和新功能P2组成;新功能P由类A和子类B完成,则子类B完成新功能P2时可能原有功能P1发生故障。
解决方案:当使用继承时,遵循里氏替换原则。类B继承类A时,除了添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。
里氏替换原则通俗讲:子类可以扩展父类的功能,但不能改变父类原有的功能。
包含以下含义:
- 子类可以实现父类的抽像方法,但不能覆盖父类的非抽像方法;
- 子类中可以增加自己特有的方法;
- 当子类要重载父类的方法时,方法的形参(既要传入的参数)要比父类方法输入的参数更宽松(参数多);
- 当子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。
3、开闭原则
1、面向对象设计的基础原则,指导我们如何建立稳定灵活的系统;
2、软件设计中使用设计模式就是为了遵循开闭原则的;
3、用抽象构建框架,用实现扩展细节;
定义:一个软件实体 如类、模块和方法(函数)应该对扩展开放,对修改关闭。
问题原因:在软件的生命周期内,因为变化、升级和维护等原因对软件的圆代码进行修改时,可能给旧代码引入错误,对整个功能进行重构, 并且需要对原有代码重新测试。
解决方案:当软件需要变化时,尽量通过扩展软件实体来实现变化,而不是通过修改原有代码来实现变化。
优点:可复用性,可维护性。
4、接口隔离原则
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应建立在最小的接口上。
问题原因:类A通过接口 I 依赖类B,类D通过接口 I 依赖类D,如果接口 I 对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
解决方案:将臃肿的接口 I 拆分成多个接口,建立需要的依赖关系,即:接口隔离原则。
类A依赖接口 I 中的方法1、2、3,类B是对类A的实现;
类C依赖接口 I 中的方法1、4、5,类D是对类C的实现;
4.1、例子
接口 I:
interface I {
public void method1();
public void method2();
public void method3();
public void method4();
public void method5();
}
类A:依赖类接口调用类B
class A{
public void depend1(I i){
i.method1();
}
public void depend2(I i){
i.method2();
}
public void depend3(I i){
i.method3();
}
}
类B:实现接口
class B implements I{
public void method1() {
System.out.println("类B实现接口I的方法1");
}
public void method2() {
System.out.println("类B实现接口I的方法2");
}
public void method3() {
System.out.println("类B实现接口I的方法3");
}
//对于类B来说,method4和method5不是必需的,但是由于接口A中有这两个方法,
//所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。
public void method4() {}
public void method5() {}
}
类C:依赖接口调用类D
class C{
public void depend1(I i){
i.method1();
}
public void depend2(I i){
i.method4();
}
public void depend3(I i){
i.method5();
}
}
类D:实现接口I
class D implements I{
public void method1() {
System.out.println("类D实现接口I的方法1");
}
//对于类D来说,method2和method3不是必需的,但是由于接口A中有这两个方法,
//所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。
public void method2() {}
public void method3() {}
public void method4() {
System.out.println("类D实现接口I的方法4");
}
public void method5() {
System.out.println("类D实现接口I的方法5");
}
}
Main:
public class Main{
public static void main(String[] args){
A a = new A();
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
C c = new C();
c.depend1(new D());
c.depend2(new D());
c.depend3(new D());
}
}
结果:接口过于臃肿。
接口分离原则
接口 I1:定义方法1
interface I1 {
public void method1();
}
接口 I2:定义方法2、3
interface I2 {
public void method2();
public void method3();
}
接口 I3:定义方法4、5
interface I3 {
public void method4();
public void method5();
}
类A:依赖接口调用类B
class A{
public void depend1(I1 i){
i.method1();
}
public void depend2(I2 i){
i.method2();
}
public void depend3(I2 i){
i.method3();
}
}
类B:实现接口 I1,I2
class B implements I1, I2{
public void method1() {
System.out.println("类B实现接口I1的方法1");
}
public void method2() {
System.out.println("类B实现接口I2的方法2");
}
public void method3() {
System.out.println("类B实现接口I2的方法3");
}
}
类C:依赖接口调用类D
class C{
public void depend1(I1 i){
i.method1();
}
public void depend2(I3 i){
i.method4();
}
public void depend3(I3 i){
i.method5();
}
}
类D:实现接口 I1,I3
class D implements I1, I3{
public void method1() {
System.out.println("类D实现接口I1的方法1");
}
public void method4() {
System.out.println("类D实现接口I3的方法4");
}
public void method5() {
System.out.println("类D实现接口I3的方法5");
}
}
5、依赖倒转原则
定义:高层模块不应该依赖底层模块,两者都应依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
问题原因:类A直接依赖类B,若要将类A改为依赖类C,则必须要更改类A的代码,带来极高的风险。
类A一般是高层模块,负责复杂的业务逻辑;
类B和类C是底层模块,负责基本的原子操作。
解决方案:将类A改为依赖接口 I,类B和类C各自实现接口 I,类A通过接口 I 间接联系类B和类C。
5.1、例子
数据访问抽像层
编写业务代码的时候,不应该直接在业务代码处直接用jdbc操作数据库,而是通过数据访问层的抽象接口中间接访问,操作层再对接口进行实现。
数据访问操作层对代码进行更改则不会影响业务代码
6、迪米特法则
定义:软件实体应尽可能少的与其他实体发生相互作用。
降低软件类之间的耦合
问题原因:类与类之间关系越密切耦合度越高,一个类发生改变时,对另一个类的影响越大。
解决方案:尽量降低类与类之间的耦合度。
部分内容从csdn作者@割韭韭转载