设计模式(3)

76 阅读2分钟

第4节 依赖倒转原则

4.1 基本介绍

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象层。低层模块尽量都要有抽象类或者接口,即,类 A 是一个子类,其上面应该都有接口。例如,在 Java 中,抽象指的是接口或抽象类,细节就是具体的实现类
  • 依赖倒转的核心思想面向接口编程
    • 抽象不应该依赖细节,细节应该依赖抽象
    • 使用接口或抽象的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成
  • 依赖倒转的理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多
  • 继承时应该遵循里氏替换原则(下一节会介绍里式替换原则)

4.2 需求介绍

完成 Person 接收消息的功能

4.3 传统写法

/**
 * demo:不使用依赖倒转原则
 */
public class Client {
  public static void main(String[] args) {
    Person person = new Person();
    person.receive(new Email());
  }
}

class Email {
  public String getInfo() {
    return "[电子邮件] hello world";
  }
}

/**
 * 完成 Person 接收电子邮件的功能
 */
class Person {
  public void receive(Email email) {
    System.out.println(email.getInfo());
  }
}

问题:receive()方法的参数是 Email,即,直接依赖了一个具体的类。如果现在获取的对象是「微信」、「短信」等,就需要新增类,而且也需要修改(重写/重载)Person 中的 receive() 方法

4.4 解决方案

引入一个抽象的接口 IReceiver(表示接收者),这样 Person 类与接口 IReceiver 发生依赖。因为 Email、Weixin 等属于接收者的范围,它们各自实现 IReceiver 接口就行,这样就符合依赖倒转原则

public class Client {
  public static void main(String[] args) {
    Person person = new Person();
    person.receive(new Email());
    person.receive(new Wechat());
  }

}

// 定义接口
interface IReceiver {
  String getInfo();
}

class Email implements IReceiver {
  @NonNull
  public String getInfo() {
    return "[电子邮件] hello Email";
  }
}

// 增加微信消息
class Wechat implements IReceiver {
  public String getInfo() {
    return "[微信] hello Wechat";
  }
}

class Person {
  // receive 方法依赖接口
  public void receive(IReceiver receiver) {
    System.out.println(receiver.getInfo());
  }
}

4.4 依赖的传递方式

  • 通过接口实现依赖的传递
  • 通过构造方法实现依赖的传递
  • 通过 Setter 方法实现依赖的传递