一篇文章了解java中依赖注入
什么是依赖(Dependency)?
依赖是类与类之间的连接,依赖关系表示一个类依赖于另一个类的定义,通俗来讲就是一种需要,例如一个人(Person)可以买车(Car)和房子(House),Person类依赖于Car类和House类
public static void main(String ... args){
//TODO:
Person person = new Person();
person.buy(new House());
person.buy(new Car());
}
static class Person{
//表示依赖House
public void buy(House house){}
//表示依赖Car
public void buy(Car car){}
}
static class House{
}
static class Car{
}
依赖倒置 (Dependency inversion principle)
依赖倒置是面向对象设计领域的一种软件设计原则
通俗来讲: 依赖倒置原则的本质就是通过抽象(接口或抽象类)使个各类或模块的实现彼此独立,互不影响,实现模块间的松耦合。也就是我们经常所说的面向接口编程
在平常的开发中,我们大概都会这样编码。
public class Person {
private Bike mBike;
private Car mCar;
private Train mTrain;
public Person(){
mBike = new Bike();
//mCar = new Car();
// mTrain = new Train();
}
public void goOut(){
System.out.println("出门啦");
mBike.drive();
//mCar.drive();
// mTrain.drive();
}
public static void main(String ... args){
//TODO:
Person person = new Person();
person.goOut();
}
}
如果我们要换出行方式,不仅要换Person构造方法里的代码,还是换goOut里的代码,那么我们运用依赖倒置原则呢?
/**
* Driveable 是接口,所以它是抽象
*/
public interface Driveable {
void drive();
}
/**
* 而 Bike 实现了接口,它们被称为具体。
*/
public class Bike implements Driveable {
@Override
public void drive() {
System.out.println("Bike drive");
}
}
/**
* 而 Car实现了接口,它们被称为具体。
*/
public class Car implements Driveable {
@Override
public void drive() {
System.out.println("Car drive.");
}
}
public class Person {
// private Bike mBike;
private Car mCar;
private Train mTrain;
private Driveable mDriveable;
public Person(){
// mBike = new Bike();
//mCar = new Car();
mDriveable = new Train();
}
public void goOut(){
System.out.println("出门啦");
mDriveable.drive();
//mCar.drive();
// mTrain.drive();
}
public static void main(String ... args){
//TODO:
Person person = new Person();
person.goOut();
}
}
我们用接口Driveable来创建对象,只要改变Person构造函数里的代码,就不要改goOut里的代码了,那么我们可不可以在外界使用Person类的使用,不去改变Person类里面的内容呢?这就要用到控制反转了
控制反转 (IoC)
控制反转 IoC 是 Inversion of Control的缩写,意思就是对于控制权的反转,对么控制权是什么控制权呢?
Person自己掌控着内部 mDriveable 的实例化。
现在,我们可以更改一种方式。将 mDriveable 的实例化移到 Person 外面。
public class Person2 {
private Driveable mDriveable;
public Person2(Driveable driveable){
this.mDriveable = driveable;
}
public void goOut(){
System.out.println("出门啦");
mDriveable.drive();
//mCar.drive();
// mTrain.drive();
}
public static void main(String ... args){
//TODO:
Person2 person = new Person2(new Car());
person.goOut();
}
}
我们通过Person的构造将Driveable传给Person类,就这样无论出行方式怎么变化,Person 这个类都不需要更改代码了。在上面代码中,Person 把内部依赖的创建权力移交给了 Person2这个类中的 main() 方法。也就是说 Person 只关心依赖提供的功能,但并不关心依赖的创建。
依赖注入(Dependency injection)
依赖注入,也经常被简称为 DI,其实在上一节中,我们已经见到了它的身影。它是一种实现 控制反转(IoC )的手段。什么意思呢?
为了不因为依赖实现的变动而去修改 Person,也就是说以可能在 Driveable 实现类的改变下不改动 Person 这个类的代码,尽可能减少两者之间的耦合。我们需要采用上一节介绍的 IoC 模式来进行改写代码。
这个需要我们移交出对于依赖实例化的控制权,那么依赖怎么办?Person 无法实例化依赖了,它就需要在外部(IoC 容器)赋值给它,这个赋值的动作有个专门的术语叫做注入(injection),需要注意的是在 IoC 概念中,这个注入依赖的地方被称为 IoC 容器,但在依赖注入概念中,一般被称为注射器 (injector)。
表达通俗一点就是:我不想自己实例化依赖,你(injector)创建它们,然后在合适的时候注入给我
实现依赖注入有 3 种方式:
- 构造函数中注入
- setter 方式注入
- 接口注入
/**
* 接口方式注入
* 接口的存在,表明了一种依赖配置的能力。
*/
public interface DepedencySetter {
void set(Driveable driveable);
}
public class Person2 implements DepedencySetter {
//接口方式注入
@Override
public void set(Driveable driveable) {
this.mDriveable = mDriveable;
}
private Driveable mDriveable;
//构造函数注入
public Person2(Driveable driveable){
this.mDriveable = driveable;
}
//setter 方式注入
public void setDriveable(Driveable mDriveable) {
this.mDriveable = mDriveable;
}
public void goOut(){
System.out.println("出门啦");
mDriveable.drive();
//mCar.drive();
// mTrain.drive();
}
public static void main(String ... args){
//TODO:
Person2 person = new Person2(new Car());
person.goOut();
}
}