依赖倒置原则(Dependence Inversion Principle)

定义:
1、高层模块不应该依赖底层模块,二者都应该依赖抽象。

2、抽象不应该依赖细节,细节应该依赖抽象。

3、依赖倒置的中心思想是面向接口编程。

4、依赖倒置原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定的多。

5、使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类来完成。

说明:
高层模块和低层模块容易理解,每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。那什么是抽象,什么又是细节呢?在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以加上一个关键字new产生一个对象。依赖倒置原则在Java语言中的表现就是:
1、模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
2、接口或抽象类不依赖于实现类;
3、实现类依赖接口或抽象类。

举例说明:
简单实现一个人开车的例子:
类图如下:
在这里插入图片描述
简单实现:

public class Driver {
	//司机的主要职责就是驾驶汽车 
	public void drive(Benz benz){
		benz.run();
	}
}
public class Benz {
	//汽车肯定会跑 
	public void run(){
		System.out.println("奔驰汽车开始运行...")}
}
public class Client { 
	public static void main(String[] args) {
		Driver zhangSan = new Driver();
 		Benz benz = new Benz();
 		//张三开奔驰车
 		zhangSan.drive(benz);
 	}
}

这个时候需求变了,比如我们想要张三(此张三非法外狂徒)开宝马怎么办?
首先我们要实现一辆宝马:

public class BMW {
	//宝马车当然也可以开动了
	public void run(){
		System.out.println("宝马汽车开始运行...");
	}
}

可是现在张三可以开这辆宝马?很明显是不可以的,那么我们的设计这时候就出现了问题。
接下来我们引入依赖倒置来解决问题:
首先我们重新设计一个类图:
在这里插入图片描述
建立两个接口:IDriver和ICar,分别定义了司机和汽车的各个职能,司机就是驾驶汽车,必须实现drive()方法:

public interface IDriver {
	//是司机就应该会驾驶汽车
 	public void drive(ICar car);
}

具体实现:

public class Driver implements IDriver{
	//司机的主要职责就是驾驶汽车
	public void drive(ICar car){
		car.run();
	}
}

在IDriver中,通过传入ICar接口实现了抽象之间的依赖关系,Driver实现类也传入了ICar接口,至于到底是哪个型号的Car需要在高层模块中声明。
接口以及具体实现:

public interface ICar {
	//是汽车就应该能跑
	public void run(); 
}
public class Benz implements ICar{
	//汽车肯定会跑
	public void run(){
 		System.out.println("奔驰汽车开始运行...");
 	}
}
 
public class BMW implements ICar{
	//宝马车当然也可以开动了
	public void run(){
		System.out.println("宝马汽车开始运行...");
	}
}

在业务场景中,我们贯彻“抽象不应该依赖细节”,也就是我们认为抽象(ICar接口)不依赖BMW和Benz两个实现类(细节),因此我们在高层次的模块中应用都是抽象。

public class Client {
	public static void main(String[] args) {
		IDriver zhangSan = new Driver();
		ICar benz = new Benz();
		//张三开奔驰车
		zhangSan.drive(benz);
	}
}

Client属于高层业务逻辑,它对低层模块的依赖都建立在抽象上,zhangSan的显示类型是IDriver,benz的显示类型是ICar,也许你要问,在这个高层模块中也调用到了低层模块,比如new Driver()和new Benz()等,如何解释?确实如此,zhangSan的显示类型是IDriver,是一个接口,是抽象的,非实体化的,在其后的所有操作中,zhangSan都是以IDriver类型进行操作,屏蔽了细节对抽象的影响。当然,张三如果要开宝马车,也很容易,我们只要修改业务场景类就可以。

public class Client {
	public static void main(String[] args) {
		IDriver zhangSan = new Driver();
		ICar bmw = new BMW();
		//张三开宝马车
		zhangSan.drive(bmw);
	}
}

在新增加低层模块时,只修改了业务场景类,也就是高层模块,对其他低层模块如Driver类不需要做任何修改,业务就可以运行,把“变更”引起的风险扩散降低到最小。

依赖倒置的写法:
接口声明依赖对象: 在接口的方法中声明依赖对象,就如上面的例子。
Setter方法传递依赖对象: 在抽象中设置setter方法声明依赖关系,依照依赖注入的说法就是setter依赖注入,按照这种方式的注入,IDriver和Driver的程序修改后如下所示:

public interface IDriver{
  //注入
  public void setCar(ICar car);

  public void drive();
}

public class Driver implements IDriver{
  private ICar car;

  public void setCar(ICar car){
    this.car = car;
  }

  public void drive(){
    this.car.run();
  }
}

构造函数传递依赖对象: 在类中通过构造函数声明依赖对象,按照依赖注入的说法这种方式叫做构造函数注入,按照这种方式的注入,IDriver和Driver的程序修改后如下所示:

//将司机模块抽象为一个接口
public interface IDriver {
     public void drive();
}

public class Driver implements IDriver{
    private ICar car;

    //注入
    public Driver(ICar car){
      this.car = car;
    }

    public void drive(ICar car){
        this.car.run();
    }
}

依赖倒转原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合,我们可以通过下面的操作实现这一原则:
1、每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备。
这是依赖倒置的基本要求,接口和抽象类都是属于抽象的,有了抽象才可能依赖倒置。
3、变量的显示类型尽量是接口或者是抽象类。
4、任何类都不应该从具体类派生。
5、尽量不要覆写基类的方法。
6、结合里氏替换原则使用。

总结:
依赖倒置原则核心就是面向接口编程

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值