• 高层模块不应该依赖底层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转的中心思想是面向接口编程
  • 依赖倒转原则是基于这么的设计理念:相对于细节的多变性,抽象的东西需要稳定的多。以抽象为基础的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。
  • 使用**接口或抽象类的目的是制定好规范**,而不涉及任何具体的操作, 把展现细节的人物交给他们的实现类去完成


案例

  1. public class DependecyInversion {
  2. public static void main(String[] args) {
  3. Person person = new Person();
  4. person.receive(new Email());
  5. }
  6. }
  7. class Email {
  8. public String getInfo() {
  9. return "电子邮件信息: hello,world";
  10. }
  11. }
  12. //完成Person接收消息的功能
  13. //方式1分析
  14. //1. 简单,比较容易想到
  15. //2. 如果我们获取的对象是 微信,短信等等,则新增类,同时Perons也要增加相应的接收方法
  16. //3. 解决思路:引入一个抽象的接口IReceiver, 表示接收者,
  17. // 这样Person类与接口IReceiver发生依赖,因为Email, WeiXin 等等属于接收的范围,
  18. // 他们各自实现IReceiver 接口就ok, 这样我们就符号依赖倒转原则
  19. class Person {
  20. public void receive(Email email ) {
  21. System.out.println(email.getInfo());
  22. }
  23. }

优化一 :接口替换细节

  1. //定义接口
  2. interface IReceiver {
  3. public String getInfo();
  4. }
  5. class Email implements IReceiver {
  6. public String getInfo() {
  7. return "电子邮件信息: hello,world";
  8. }
  9. }
  10. //增加微信
  11. class WeiXin implements IReceiver {
  12. public String getInfo() {
  13. return "微信信息: hello,ok";
  14. }
  15. }
  16. //方式2
  17. class Person {
  18. //这里我们是对接口的依赖
  19. public void receive(IReceiver receiver ) {
  20. System.out.println(receiver.getInfo());
  21. }
  22. }

依赖关系传递的三种方式和应用案例

接口传递

  1. // 方式1: 通过接口传递实现依赖
  2. // 开关的接口
  3. interface IOpenAndClose {
  4. public void open(ITV tv); //抽象方法,接收接口
  5. }
  6. interface ITV { //ITV接口
  7. public void play();
  8. }
  9. class ChangHong implements ITV {
  10. @Override
  11. public void play() {
  12. // TODO Auto-generated method stub
  13. System.out.println("长虹电视机,打开");
  14. }
  15. }
  16. class OpenAndClose implements IOpenAndClose {
  17. public void open(ITV tv) {
  18. tv.play();
  19. }
  20. }

构造方法传递

  1. // 方式2:通过构造方法依赖传递
  2. interface IOpenAndClose {
  3. public void open(); //抽象方法
  4. }
  5. interface ITV { //ITV接口
  6. public void play();
  7. }
  8. class OpenAndClose implements IOpenAndClose {
  9. public ITV tv; //成员
  10. public OpenAndClose(ITV tv) { //构造器
  11. this.tv = tv;
  12. }
  13. public void open() {
  14. this.tv.play();
  15. }
  16. }
  17. class ChangHong implements ITV {
  18. @Override
  19. public void play() {
  20. // TODO Auto-generated method stub
  21. System.out.println("长虹电视机,打开");
  22. }
  23. }

setter方式传递

  1. // 方式3 , 通过setter方法传递
  2. interface IOpenAndClose {
  3. public void open(); // 抽象方法
  4. public void setTv(ITV tv);
  5. }
  6. interface ITV { // ITV接口
  7. public void play();
  8. }
  9. class OpenAndClose implements IOpenAndClose {
  10. private ITV tv;
  11. public void setTv(ITV tv) {
  12. this.tv = tv;
  13. }
  14. public void open() {
  15. this.tv.play();
  16. }
  17. }
  18. class ChangHong implements ITV {
  19. @Override
  20. public void play() {
  21. // TODO Auto-generated method stub
  22. System.out.println("长虹电视机,打开");
  23. }
  24. }

调用

 public static void main(String[] args) {
        // TODO Auto-generated method stub
        ChangHong changHong = new ChangHong();
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.open(changHong);

        //通过构造器进行依赖传递
        OpenAndClose openAndClose = new OpenAndClose(changHong);
        openAndClose.open();
        //通过setter方法进行依赖传递
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.setTv(changHong);
        openAndClose.open();

    }

总结

注意事项和细节

  • 底层模块尽量都要有抽象类或接口,或者俩者都有,程序稳定性更好。
  • 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
  • 继承时遵循里氏替换原则