什么是依赖(Dependency)?
@H_301_0@依赖是一种关系,通俗来讲就是一种需要。@H_301_0@程序员需要电脑,因为没有电脑程序员就没有办法编写代码,所以说程序员依赖电脑,电脑被程序员依赖。 @H_301_0@在面向对象编程中,代码可以这样编写。
class Coder {
Computer mComputer;
public Coder () {
mComputer = new Computer();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
依赖倒置 (Dependency inversion principle)
@H_301_0@依赖倒置是面向对象设计领域的一种软件设计原则。软件设计有 6 大设计原则,合称 SOLID。 @H_301_0@有人会有疑惑,设计原则有什么用呢? @H_301_0@设计原则是前辈们总结出来的经验,你可以把它们看作是内功心法。 @H_301_0@只要你在平常开发中按照设计原则进行编码,假以时日,你编程的功力将会大增。 @H_301_0@依赖倒置原则的定义如下:
@H_301_0@乍一看,这会让初学者摸不清头脑。这种学术性的概括语言近乎于软件行业中的哲学。可实质上,它确实称得上是哲学,现在 SOLID 几乎等同于面向对象开发中的金科玉律,但是也正因为它的高度概括、它的晦涩难懂,对于广大初学者而言这是一件非常不友好的事物。 @H_301_0@我们该怎么理解上面的定义呢?我们需要咬文嚼字,各个突破。
- 上层模块不应该依赖底层模块,它们都应该依赖于抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
什么是上层模块和底层模块?
@H_301_0@不管你承认不承认,“有人的地方就有江湖”,我们都说人人平等,但是对于任何一个组织机构而言,它一定有架构的设计有职能的划分。按照职能的重要性,自然而然就有了上下之分。并且,随着模块的粒度划分不同这种上层与底层模块会进行变动,也许某一模块相对于另外一模块它是底层,但是相对于其他模块它又可能是上层。 @H_301_0@公司管理层就是上层,CEO 是整个事业群的上层,那么 CEO 职能之下就是底层。 @H_301_0@然后,我们再以事业群为整个体系划分模块,各个部门经理以上部分是上层,那么之下的组织都可以称为底层。 @H_301_0@由此,我们可以看到,在一个特定体系中,上层模块与底层模块可以按照决策能力高低为准绳进行划分。 @H_301_0@那么,映射到我们软件实际开发中,一般我们也会将软件进行模块划分,比如业务层、逻辑层和数据层。@H_301_0@业务层中是软件真正要进行的操作,也就是做什么。
逻辑层是软件现阶段为了业务层的需求提供的实现细节,也就是怎么做。
数据层指业务层和逻辑层所需要的数据模型。 @H_301_0@因此,如前面所总结,按照决策能力的高低进行模块划分。业务层自然就处于上层模块,逻辑层和数据层自然就归类为底层。
什么是抽象和细节?
@H_301_0@抽象如其名字一样,是一件很抽象的事物。抽象往往是相对于具体而言的,具体也可以被称为细节,当然也被称为具象。 @H_301_0@比如:1. 这是一幅画。画是抽象,而油画、素描、国画而言就是具体。
2. 这是一件艺术品,艺术品是抽象,而画、照片、瓷器等等就是具体了。
3. 交通工具是抽象,而公交车、单车、火车等就是具体了。
4. 表演是抽象,而唱歌、跳舞、小品等就是具体。 @H_301_0@上面可以知道,抽象可以是物也可以是行为。 @H_301_0@具体映射到软件开发中,抽象可以是接口或者抽象类形式。
public interface Driveable {
void drive();
}
class Bike implements Driveable{
@Override
public void drive() {
// TODO Auto-generated method stub
System.out.println("Bike drive.");
}
}
class Car implements Driveable{
@Override
public void drive() {
// TODO Auto-generated method stub
System.out.println("Car drive.");
}
}
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
依赖倒置的好处
@H_301_0@在平常的开发中,我们大概都会这样编码。public class Person {
private Bike mBike;
public Person() {
mBike = new Bike();
}
public void chumen() {
System.out.println("出门了");
mBike.drive();
}
}
- 15
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person person = new Person();
person.chumen();
}
}
- 12
出门了 Bike drive.
- 3
public class Person {
private Bike mBike;
private Car mCar;
public Person() {
//mBike = new Bike();
mCar = new Car();
}
public void chumen() {
System.out.println("出门了");
//mBike.drive();
mCar.drive();
}
}
- 18
class Train implements Driveable{
@Override
public void drive() {
// TODO Auto-generated method stub
System.out.println("Train drive.");
}
}
package com.frank.test;
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 chumen() {
System.out.println("出门了");
//mBike.drive();
//mCar.drive();
mTrain.drive();
}
}
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
@H_301_0@首先是上层模块和底层模块的拆分。 @H_301_0@按照决策能力高低或者重要性划分,Person 属于上层模块,Bike、Car 和 Train 属于底层模块。 @H_301_0@上层模块不应该依赖于底层模块。
- 上层模块不应该依赖底层模块,它们都应该依赖于抽象。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
但是
public class Person {
private Bike mBike;
private Car mCar;
private Train mTrain;
public Person() {
//mBike = new Bike();
//mCar = new Car();
mTrain = new Train();
}
}
- 15
public class Person {
// private Bike mBike;
// private Car mCar;
// private Train mTrain;
private Driveable mDriveable;
public Person() {
//mBike = new Bike();
//mCar = new Car();
//mTrain = new Train();
mDriveable = new Train();
}
public void chumen() {
System.out.println("出门了");
//mBike.drive();
//mCar.drive();
//mTrain.drive();
mDriveable.drive();
}
}
- 24
出门了 Train drive.
- 2
class AirPlane implements Driveable{
@Override
public void drive() {
// TODO Auto-generated method stub
System.out.println("AirPlane fly.");
}
}
- 8
控制反转 (IoC)
@H_301_0@控制反转 IoC 是 Inversion of Control的缩写,意思就是对于控制权的反转,对么控制权是什么控制权呢?大家重新审视上面的代码。
public class Person {
// private Bike mBike;
// private Car mCar;
// private Train mTrain;
private Driveable mDriveable;
public Person() {
//mBike = new Bike();
//mCar = new Car();
//mTrain = new Train();
mDriveable = new Train();
}
public void chumen() {
System.out.println("出门了");
//mBike.drive();
//mCar.drive();
//mTrain.drive();
mDriveable.drive();
}
}
- 23
public Person() {
//mBike = new Bike();
//mCar = new Car();
//mTrain = new Train();
mDriveable = new Train();
}
- 6
现在,我们可以更改一种方式。将 mDriveable 的实例化移到 Person 外面。
public class Person {
private Driveable mDriveable;
public Person(Driveable driveable) {
this.mDriveable = driveable;
}
public void chumen() {
System.out.println("出门了");
mDriveable.drive();
}
}
- 17
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Bike bike = new Bike();
Car car = new Car();
Train train = new Train();
// Person person = new Person(bike);
// Person person = new Person(car);
Person person = new Person(train);
person.chumen();
}
}
- 19
比如上面代码,Person 不再亲自创建 Driveable 对象,它将依赖的实例化的权力交接给了 Test1。而 Test1 在 IoC 中又指代了 IoC 容器 这个概念。 @H_301_0@再举一个例子,我们到餐厅去叫外卖,餐厅有专门送外卖的外卖员,他们的使命就是及时送达外卖食品。 @H_301_0@依照依赖倒置原则,我们可以创建这样一个类。
public abstract class WaimaiYuan {
protected Food food;
public WaimaiYuan(Food food) {
this.food = food;
}
abstract void songWaiMai();
}
public class Xiaohuozi extends WaimaiYuan {
public Xiaohuozi(Food food) {
super(food);
}
@Override
void songWaiMai() {
System.out.println("我是小伙子,为您送的外卖是:"+food);
}
}
public class XiaoGuniang extends WaimaiYuan {
public XiaoGuniang(Food food) {
super(food);
}
@Override
void songWaiMai() {
System.out.println("我是小姑娘,为您送的外卖是:"+food);
}
}
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
public abstract class Food {
protected String name;
@Override
public String toString() {
return name;
}
}
public class PijiuYa extends Food {
public PijiuYa() {
name = "啤酒鸭";
}
}
public class DuojiaoYutou extends Food {
public DuojiaoYutou() {
name = "剁椒鱼头";
}
}
- 26
public class Restaurant {
public static void peican(int orderid,int flowid) {
WaimaiYuan person;
Food food;
if ( orderid == 0) {
food = new PijiuYa();
} else {
food = new DuojiaoYutou();
}
if ( flowid % 2 == 0 ) {
person = new Xiaohuozi(food);
} else {
person = new XiaoGuniang(food);
}
person.songWaiMai();
}
}
- 24
flowid 是订单的流水号码。 餐厅根据流水编码的不同来指派小伙子或者小姑娘来送外卖,编写测试代码。
public class IocTest {
public static void main(String[] args) {
Restaurant.peican(0,0);
Restaurant.peican(0,1);
Restaurant.peican(1,2);
Restaurant.peican(0,3);
Restaurant.peican(1,4);
Restaurant.peican(1,5);
Restaurant.peican(1,6);
Restaurant.peican(0,7);
Restaurant.peican(0,8);
}
}
- 17
我是小伙子,为您送的外卖是:啤酒鸭 我是小姑娘,为您送的外卖是:啤酒鸭 我是小伙子,为您送的外卖是:剁椒鱼头 我是小姑娘,为您送的外卖是:剁椒鱼头 我是小伙子,为您送的外卖是:啤酒鸭
- 10
依赖注入(Dependency injection)
@H_301_0@依赖注入,也经常被简称为 DI,其实在上一节中,我们已经见到了它的身影。它是一种实现 IoC 的手段。什么意思呢?public class Person {
private Driveable mDriveable;
public Person() {
mDriveable = new Train();
}
public void chumen() {
System.out.println("出门了");
mDriveable.drive();
}
}
- 19
public class Person {
private Driveable mDriveable;
public Person() {
mDriveable = new Bike();
//mDriveable = new Car();
//mDriveable = new Train();
}
public void chumen() {
System.out.println("出门了");
mDriveable.drive();
}
}
- 19
1. 构造函数中注入
2. setter 方式注入
3. 接口注入 @H_301_0@我们现在一一观察这些方式
构造函数注入
public class Person {
private Driveable mDriveable;
public Person(Driveable driveable) {
this.mDriveable = driveable;
}
public void chumen() {
System.out.println("出门了");
mDriveable.drive();
}
}
- 18
缺点:后期无法更改依赖。
setter 方式注入
public class Person {
private Driveable mDriveable;
public Person() {
}
public void chumen() {
System.out.println("出门了");
mDriveable.drive();
}
public void setDriveable(Driveable mDriveable) {
this.mDriveable = mDriveable;
}
}
- 21
缺点:Person 对象运行时,可能会存在依赖项为 null 的情况,所以需要检测依赖项的状态。
public void chumen() {
if ( mDriveable != null ) {
System.out.println("出门了");
mDriveable.drive();
}
}
- 9
接口方式注入
public interface DepedencySetter {
void set(Driveable driveable);
}
class Person implements DepedencySetter{
private Driveable mDriveable;
public void chumen() {
if ( mDriveable != null ) {
System.out.println("出门了");
mDriveable.drive();
}
}
@Override
public void set(Driveable driveable) {
this.mDriveable = mDriveable;
}
}
- 23
总结
- 依赖倒置是面向对象开发领域中的软件设计原则,它倡导上层模块不依赖于底层模块,抽象不依赖细节。
- 依赖反转是遵守依赖倒置这个原则而提出来的一种设计模式,它引入了 IoC 容器的概念。
- 依赖注入是为了实现依赖反转的一种手段之一。
- 它们的本质是为了代码更加的“高内聚,低耦合”。
Dependency inversion principle
Inversion_of_control
Dependency injection @H_301_0@下篇文章,我会讲解在 Android 开发中很出名的依赖注入框架 Dagger2。
@H_301_0@转自:https://blog.csdn.net/briblue/article/details/75093382