依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;
抽象不应该依赖细节,细节应该依赖抽象。
其核心思想是:要面向接口编程,不要面向实现编程。
依赖倒置原则的目的是通过要面向接口的编程来降低类间的耦合性,所以我们在实际编程中只要遵循以下4点,就能在项目中满足这个规则。
1.每个类尽量提供接口或抽象类,或者两者都具备。
2.变量的声明类型尽量是接口或者是抽象类。
3.任何类都不应该从具体类派生。
4.使用继承时尽量遵循里氏替换原则。
下面给出一个具体的例子进行说明。
UML类图:
有三种方式可以实现依赖倒置:
方式1:使用接口传递,QQPlayer实现了IPlayerA,而IPlayerA依赖于IFile,这样通过IPlayerA将IFile传递给了QQPlayer类的对象。
方式2:使用构造函数,NEPlayer实现了IPlayerB,并且NEPlayer关联IFile (实线箭头),通过构造函数将IFile的实例传递给NEPlayer类的对象。
方式3:使用属性Setter方法,KWPlayer实现了IPlayerB,并且KWPlayer关联IFile (实现箭头),通过私有变量的Setter方法setFile(),将IFile实例传递给KWPlayer类的对象。
下面是根据UML类图实现的Java代码:
1 public interface IFile { 2 public String getFileName(); 3 } 4 5 public class MusicFile implements IFile { 6 7 private String fileName; 8 9 public MusicFile(String fileName) { 10 this.fileName = fileName; 11 } 12 13 @Override 14 public String getFileName() { 15 return this.fileName; 16 } 17 18 } 19 20 public interface IPlayerA { 21 public void playFile(IFile file); 22 } 23 24 public interface IPlayerB { 25 public void play(); 26 } 27 28 public class QQPlayer implements IPlayerA { 29 @Override 30 public void playFile(IFile file) { 31 String fileName = file.getFileName(); 32 System.out.println("QQ音乐开始播放歌曲:" + fileName); 33 } 34 } 35 36 public class NEPlayer implements IPlayerB { 37 private IFile file; 38 39 public NEPlayer(IFile file) { 40 this.file = file; 41 } 42 43 @Override 44 public void play() { 45 String fileName = this.file.getFileName(); 46 System.out.println("网易音乐开始播放歌曲:" + fileName); 47 } 48 } 49 50 public class KWPlayer implements IPlayerB { 51 52 private IFile file; 53 54 public void setFile(IFile file) { 55 this.file = file; 56 } 57 58 @Override 59 public void play() { 60 // TODO Auto-generated method stub 61 String fileName = this.file.getFileName(); 62 System.out.println("酷我音乐开始播放歌曲:" + fileName); 63 } 64 } 65 66 public class Client { 67 public static void main(String[] args) { 68 byInterface(); 69 byConstruct(); 70 bySetter(); 71 } 72 73 // 方式1:接口方式 74 public static void byInterface() { 75 IFile file = new MusicFile("盗将行"); 76 QQPlayer player = new QQPlayer(); 77 player.playFile(file); 78 } 79 80 // 方式2:构造函数方式 81 public static void byConstruct() { 82 IFile file = new MusicFile("出山"); 83 NEPlayer player = new NEPlayer(file); 84 player.play(); 85 } 86 87 // 方式3:属性Setter方式 88 public static void bySetter() { 89 IFile file = new MusicFile("芒种"); 90 KWPlayer player = new KWPlayer(); 91 player.setFile(file); 92 player.play(); 93 } 94 }