高层模块不应当依赖底层模块, 二者都应该依赖其抽象(抽象不应当依赖细节, 细节应当依赖抽象)
这个定义比较抽象, 直接上代码
- 现在我来实现一个熊猫吃东西的业务,可以吃的东西有bamboo shoots(竹笋) bamboo leaves(竹叶)等
public class Panda1 {
public void eatBambooShoots(){
System.out.println("eat bamboo shoots");
}
public void eatBambooLeaves(){
System.out.println("eat bamboo leaves");
}
}
ok, 代码已经完成了, 来测试一下
public class Test1 {
public static void main(String[] args) {
Panda1 panda = new Panda1();
panda.eatBambooLeaves();
panda.eatBambooShoots();
}
}
run, 没问题. 但是这时候问题来了, 熊猫馆又给熊猫增加了一种新的食品food3 (- . -emmm...名字起得随意了点,不要在意这些细节)
这时候我不得不修改 Panda1这个类, 增加一个新的方法eatFood3,同时修改客户端 增加panda.eatFood3();
在这个例子中 Test作为调用者 ,是 高层模块, Panda类作为被调用者 是底层模块, 当Panda新增加一种食品时Test作为高层必须要等底层的Panda增加方法之后才能实现业务,很明显依赖了底层的模块,这就违背了依赖倒置原则.
- 我们来重新设计一版
将熊猫的食物抽象出来
public interface PandaFood {
String getFoodName();
}
Panda2 吃食物,吃的食物由调用者传入
public class Panda2 {
public void eatPandaFood(PandaFood pandaFood){
System.out.println("eat " + pandaFood.getFoodName());
}
}
定义食物
public class BambooLeaves implements PandaFood {
@Override
public String getFoodName() {
return "bamboo leaves";
}
}
public class BambooShoots implements PandaFood {
@Override
public String getFoodName() {
return "bamboo shoots";
}
}
客户端Test2
public class Test2 {
public static void main(String[] args) {
Panda2 panda = new Panda2();
PandaFood leaves = new BambooLeaves();
PandaFood shoots = new BambooShoots();
panda.eatPandaFood(leaves);
panda.eatPandaFood(shoots);
}
}
run ... 得到跟Test1一样的结果, 这时新增了一种食物Food3 ,我们只需要新增加一种PandaFood的实现并修改Test2即可,完全无需对底层的类做任何修改, 这里最关键的部分是对PandaFood的抽象,并且在Panda2类中的eatFood方法中的入参是PandaFood, 即面向接口编程,而不是面向细节.
第三种模式, 通过构造器或set方法注入的方式(Spring常用的模式)
public class Panda3 {
private PandaFood pandaFood;
public Panda3(PandaFood pandaFood) {
this.pandaFood = pandaFood;
}
public void setPandaFood(PandaFood pandaFood) {
this.pandaFood = pandaFood;
}
public void eatPandaFood(){
System.out.println("eat " + pandaFood.getFoodName());
}
}
这样 调用 eatPandaFood 时就无需关心到底要吃什么, 代码的耦合性更低, 更容易维护
网友评论