阅读本文大概需要 8 分钟
-
前言
-
1.依赖倒转概述
-
1.1. 变量的静态类型和真实类型
-
1.2. 引用类型和抽象类型
-
-
2.依赖倒转原则体现
-
2.1. 工厂方法模式中的体现
-
2.2. 模板方法模式中的体现
-
前言
1. 依赖倒转原则概述
依赖倒转的核心是:要依赖于抽象,不要依赖于具体。对这句话的理解抽象就是我们平时接触的接口,接口做的是确定我们要做的目标,而具体则是指实现这个接口的类,这个类中做了实现目标的具体过程。
所以依赖倒转主要关注的就是抽象层与具体层,抽象层包含宏观的逻辑以及整体的决定,而具体层则是实现这个逻辑以及决定的具体算法。而依赖倒转就是将具体层依赖于抽象层,也就是具体层的实现过程需要依赖抽象层来确定目标。也就形成了所谓的依赖倒转。
依赖倒转放到Java中来说的话就是要依赖接口编程,不要依赖实现编程。以平时开发为例,我们在service层时会先写一个service接口,然后再写一个对应的serviceImpl的实现类,这样实现类依赖上层接口,实现具体的实现过程。
针对接口编程来说,应当使用Java接口和抽象Java类进行变量的声明、参量的类型声明、方法的返回类型声明以及数据类型的转换等。不应当使用具体的Java类进行变量的类型声明、参量的类型声明、方法的返回类型声明以及数据类型的转换等。这些声明都应当放到接口中实现。
同时要保证一个具体Java类应当只实现Java接口和抽象Java类中声明过的方法,而不应当给出多余的方法。
通过这样的一种方式保证一个系统内的实体之间的关系的灵活性。
1.1. 变量的静态类型和真实类型
一个变量在被声明时的类型叫做变量的静态类型,变量所引用的对象的真实类型叫做变量的实际类型。
例如:
List list = new Vector();
上面的代码中list的静态类型是List,而它的实际类型是Vector。
1.2. 引用类型和抽象类型
在Java中需要引用一个对象时,如果这个对象有一个抽象类型的话,应当使用这个抽象类型作为变量的静态类型。这就是针对接口编程的含义。比如上面的代码中也可以写成
Vector list = new Vector();
但是Vector是一个具体的类,而List则是一个接口,并且如果后续需要转换为List类型时则仅仅改动很少就可以。
这也是一种依赖倒转的实现。要依赖接口编程,而不是依赖实现编程。
2. 依赖倒转原则体现
既然说我们要遵循依赖倒转,那么在平时开发中如何实现这种依赖倒转呢?其实如果考虑前面的里氏代换原则,那么依赖倒转既然要依赖接口编程,那么就一定需要遵循里氏代换原则,因为既然依赖接口那么必然要在引用到基类的地方都可以改换成其子类。所以各种原则都是相辅相成。
根据前面的例子可以知道我们平时创建对象时都是通过new的方式进行创建,其实如果完全按照依赖倒转原则来考虑new方式创建对象,那么其实new方式已经违反了依赖倒转原则,因为我们创建的对象都是一个实际类型,比如我创建一个Student,那么我需要new一个Student时,需要进行一下方式,而Student并不是一个抽象类型。
所以这样的方式是违反依赖倒转原则,那么有什么方式可以遵循依赖倒转呢?其实首先就可以想到工厂模式。
Student stu = new Student();
2.1. 工厂方法模式中的体现
工厂模式的核心就是将创建对象的任务交给工厂,而工厂则可以作为一个抽象类型进行实现。通过工厂模式创建对象时就可以实现依赖抽象类型进行创建对象。
/**
* 抽象类型对象
*/
public interface Animal {
/**
* 对象方法
*/
public void eat();
}
/**
* 实际类型依赖抽象类型
*/
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("我要吃骨头");
}
}
/**
* 实际类型依赖抽象类型
*/
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("我要吃鱼");
}
}
/**
* 创建对象类,最后创建的对象为抽象类型Animal
*/
public class FactoryInterface {
/**
* 通过对应类型创建对象
* @param type
* @return
*/
public Animal creatEat(int type) {
if (type == 1) {
return new Dog();
}else if (type == 2) {
return new Cat();
}
return null;
}
}
public class FactoryDemo {
public static void main(String[] args) {
FactoryInterface factoryInterface = new FactoryInterface();
Animal animal = factoryInterface.creatEat(1);
animal.eat();
FactoryInterface factoryInterface2 = new FactoryInterface();
Animal animal2 = factoryInterface2.creatEat(2);
animal2.eat();
}
}
从以上代码中可以看到,在demo中创建对象的接收类型是抽象类型Animal,但是因为Cat与Dog类型都已经依赖于Animal,这样保证了依赖倒转原则。
2.2. 模板方法模式中的体现
模板方法故名思意就是通过一个模板方法来实现一系列操作,而不需要通过对象具体去实现。
/**
* 抽象类型Game
*/
public abstract class Game {
public abstract void startGame();
public abstract void endGame();
/**
* 模板方法
*/
public final void play() {
startGame();
endGame();
}
}
/**
* 英雄联盟具体实现类
*/
public class LeagueOfLegends extends Game{
@Override
public void startGame() {
System.out.println("欢迎来到英雄联盟");
}
@Override
public void endGame() {
System.out.println("victory");
}
}
/**
* 魔兽世界具体类
*/
public class WorldOfWarcraft extends Game {
@Override
public void startGame() {
System.out.println("颤抖吧!凡人!");
}
@Override
public void endGame() {
System.out.println("生命本身毫无意义,只有死亡才能让你了解人性的真谛!");
}
}
/**
* 测试类
*/
public class TemplateDemo {
public static void main(String[] args) {
//英雄联盟开始,通过Game抽象类实现具体的模板方法
Game lol = new LeagueOfLegends();
lol.play();
System.out.println("==========================");
//魔兽世界开始,通过Game抽象类实现具体的模板方法
Game wow = new WorldOfWarcraft();
wow.play();
}
}
以上代码中可以看到创建对象时并不是直接创建的对象,而是通过抽象类型Game进行创建,这样就遵守了依赖倒转原则,同时也提高了代码的灵活性,直接在抽象类中定义模板方法即可!
参考资料:
1、《Java与模式》