1.单一职责原则

不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
遵循单一职责原的优点有:
可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
提高类的可读性,提高系统的可维护性;
变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。

举例说明,用一个类描述动物呼吸这个场景:

  1. class Animal{
  2. public void breathe(String animal){
  3. System.out.println(animal+"呼吸空气");
  4. }
  5. }
  6. public class Client{
  7. public static void main(String[] args){
  8. Animal animal = new Animal();
  9. animal.breathe("牛");
  10. animal.breathe("羊");
  11. animal.breathe("猪");
  12. }
  13. }

程序上线后,发现问题了,并不是所有的动物都呼吸空气的,比如鱼就是呼吸水的。修改时如果遵循单一职责原则,需要将Animal类细分为陆生动物类Terrestrial,水生动物Aquatic,代码如下:

  1. class Terrestrial{
  2. public void breathe(String animal){
  3. System.out.println(animal+"呼吸空气");
  4. }
  5. }
  6. class Aquatic{
  7. public void breathe(String animal){
  8. System.out.println(animal+"呼吸水");
  9. }
  10. }
  11. public class Client{
  12. public static void main(String[] args){
  13. Terrestrial terrestrial = new Terrestrial();
  14. terrestrial.breathe("牛");
  15. terrestrial.breathe("羊");
  16. terrestrial.breathe("猪");
  17. Aquatic aquatic = new Aquatic();
  18. aquatic.breathe("鱼");
  19. }
  20. }

2.里氏替换原则

定义:继承必须确保父类所拥有的性质在子类中仍然成立。
由来:通过子类来完成父类的任务,可能会产生问题。
解决:子类可以实现父类的抽象方法,但是不去Override父类的非抽象方法。这也算是某种意义上的开闭原则吧,尽量不要去影响旧有的代码,通过扩展(取新名字,而不是Override)来完成新功能。

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

3.依赖倒置原则

定义:高层模块不依赖于底层模块,两者都应该依赖于抽象,抽象不依赖于细节,细节依赖于抽象。
由来:表示层、业务逻辑层以及数据访问层之间如果分得不太清楚,各模块之间交叉调用,就会带来很强的耦合性,往往会牵一发而动全身,改动一个地方,很多地方都会受到影响,增加出错的风险。
解决:主要是通过面对接口编程,将实现细节与业务逻辑分开,它们都是通过抽象的接口来完成交互的。业务逻辑只和抽象的接口打交到,而不必关注具体的实现过程。同样实现过程也不必关注业务,它只需要关注接口即抽象即可。

依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
我们引入一个抽象的接口IReader。读物,只要是带字的都属于读物:

  1. interface IReader{
  2. public String getContent();
  3. }

Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了,代码修改为:

  1. class Newspaper implements IReader {
  2. public String getContent(){
  3. return "林书豪17+9助尼克斯击败老鹰……";
  4. }
  5. }
  6. class Book implements IReader{
  7. public String getContent(){
  8. return "很久很久以前有一个阿拉伯的故事……";
  9. }
  10. }
  11. class Mother{
  12. public void narrate(IReader reader){
  13. System.out.println("妈妈开始讲故事");
  14. System.out.println(reader.getContent());
  15. }
  16. }
  17. public class Client{
  18. public static void main(String[] args){
  19. Mother mother = new Mother();
  20. mother.narrate(new Book());
  21. mother.narrate(new Newspaper());
  22. }
  23. }

4.接口隔离原则

定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应建立在最小接口上。
由来:一个接口里完成了很多工作,但是当个功能只需要调用接口里的一小部分功能的时候,如果调用这个接口,就会做一些不必要的工作,甚至可能产生问题。
解决:一个接口尽量完成比较单一的任务,这样两个类交互时,产生的影响才会在控制范围内。

  1. interface I1 {
  2. public void method1();
  3. }
  4. interface I2 {
  5. public void method2();
  6. public void method3();
  7. }
  8. interface I3 {
  9. public void method4();
  10. public void method5();
  11. }
  12. class A{
  13. public void depend1(I1 i){
  14. i.method1();
  15. }
  16. public void depend2(I2 i){
  17. i.method2();
  18. }
  19. public void depend3(I2 i){
  20. i.method3();
  21. }
  22. }
  23. class B implements I1, I2{
  24. public void method1() {
  25. System.out.println("类B实现接口I1的方法1");
  26. }
  27. public void method2() {
  28. System.out.println("类B实现接口I2的方法2");
  29. }
  30. public void method3() {
  31. System.out.println("类B实现接口I2的方法3");
  32. }
  33. }
  34. class C{
  35. public void depend1(I1 i){
  36. i.method1();
  37. }
  38. public void depend2(I3 i){
  39. i.method4();
  40. }
  41. public void depend3(I3 i){
  42. i.method5();
  43. }
  44. }
  45. class D implements I1, I3{
  46. public void method1() {
  47. System.out.println("类D实现接口I1的方法1");
  48. }
  49. public void method4() {
  50. System.out.println("类D实现接口I3的方法4");
  51. }
  52. public void method5() {
  53. System.out.println("类D实现接口I3的方法5");
  54. }
  55. }

5.迪米特法则

定义:也称最少知道原则(Least Knowledge Principle),只和你最直接的类(成员变量、方法参数、方法返回值中的类)沟通,尽可能少地与其他实体发生交互。
由来:想降低类之间的耦合关系,相互之间尽量减少依赖关系。
解决:与越少的类相互越好,尽量做到低耦合高内聚。尽量做到模块化。

6.开闭原则

定义:软件实体应该对扩展开放,对修改关闭。
由来:一些软件生命周期很长,必然面临维护升级等变化。而新添加的代码很容易对旧有的代码造成影响,甚至给旧有的代码带来Bug。
解决:当软件代码需要进行变动时,尽量以添加新的代码来完成,而不去修改原有的代码。也即通过扩展来完成所需要的功能的添加。