依赖倒转原则:
- 高层模块不应该依赖底层模块。两个都应该依赖抽象
- 抽象不应该依赖细节。细节应该依赖抽象
字面意思:
1. 依赖:一个类需要依赖其他类
2. 倒转:不直接依赖于这个类,而是依赖其接口或抽象类
说白了,就是针对接口编程,不要针对实现编程。
不遵循依赖倒转原则的例子:
public class demo {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email{
public String getInfo(){
return "Email get info";
}
}
class Person{
public void receive(Email email){ //Person类直接依赖于Email类,没有依赖于抽象
System.out.println(email.getInfo());
}
}
如果有新的需求,需要接受微信的消息和短信的消息,如果是这种方式,则需要增加receive(WeiXin weixing) 和 receive(DuanXing duanxing)方法,
而且可以发现,高层的逻辑(即Person对象接受消息的逻辑)是不变的,但是底层逻辑(即接受什么类型的消息)一旦增加,则需要对Person进行改变,耦合度较高
遵循依赖倒转:
public class demo {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
person.receive(new WeiXin());
}
}
interface IReceiver{
String getInfo();
}
class Email implements IReceiver{
public String getInfo(){
return "Email get info";
}
}
class WeiXin implements IReceiver{
public String getInfo(){
return "WeiXin get info";
}
}
class Person{
public void receive(IReceiver receiver){ //依赖于抽象(接口)
System.out.println(receiver.getInfo());
}
}
《大话设计模式》:
面向过程的开发,为了使得常用可以复用,一般都会把常用代码写成许多函数的程序库,在做新的项目时去调用这些底层的函数就可以了。比如访问数据库,只要把访问数据库的代码写成函数,每次做新项目时去调用这些函数。这叫做:高层模块依赖底层模块
但是:
做新项目时,可能发现业务逻辑的高层模块是一样的,但客户希望使用不同数据库。我们希望使用这些高层模块,但是这些高层模块都是与底层的数据库绑定在一起,没办法复用这些高层模块。
如果是依赖于接口编程,哪怕更换数据库,只要实现不同数据库对接口的实现,高层模块依然可以服用且无需修改。
依赖传递的三种方式:
1. 通过接口传递
public class demo {
public static void main(String[] args) {
IAnimal iAnimal = new Person();
iAnimal.eat(new Orange());
}
}
interface IAnimal{
void eat(IFood food); //依赖于IFood接口
}
interface IFood{
void taste();
}
class Orange implements IFood{
@Override
public void taste() {
System.out.println("Orange taste ...");
}
}
class Person implements IAnimal{
@Override
public void eat(IFood food) {
food.taste();
}
}
2. 通过构造方法传递
public class demo {
public static void main(String[] args) {
IAnimal iAnimal = new Person(new Orange());
iAnimal.eat();
}
}
interface IAnimal{
void eat();
}
interface IFood{
void taste();
}
class Orange implements IFood{
@Override
public void taste() {
System.out.println("Orange taste ...");
}
}
class Person implements IAnimal{
IFood food;
public Person(IFood food){ //通过构造方法传递依赖关系
this.food = food;
}
@Override
public void eat() {
this.food.taste();
}
}
3. 通过setter传递
public class demo {
public static void main(String[] args) {
IAnimal iAnimal = new Person();
iAnimal.setFood(new Orange());
iAnimal.eat();
}
}
interface IAnimal{
void eat();
void setFood(IFood food); //通过set方法传入依赖关系
}
interface IFood{
void taste();
}
class Orange implements IFood{
@Override
public void taste() {
System.out.println("Orange taste ...");
}
}
class Person implements IAnimal{
IFood food;
@Override
public void setFood(IFood food){
this.food = food;
}
@Override
public void eat() {
this.food.taste();
}
}
依赖倒转原则注意事项:
- 低层模块尽量都要有抽象类或者接口
- 变量的声明类型尽量是抽象类或者接口,这样我们的变量引用和实际对象间就存在一层缓冲层(变量指向抽象类/接口,实际运行时会通过多态找到实际的类型),便于扩展
- 继承时遵循里式替换原则