concept
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions
高层模块不应该依赖低层模块,两者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象
analyse
- 每一个逻辑的实现都是原子逻辑组成的,不可分割的原子逻辑就是低层模块(一般是接口、抽象类),原子逻辑的组装就是高层模块
【高层模块】 : 调用别的方法的模块
【低层模块】:被其他方法调用的模块- 在Java语言中,【抽象】就是指接口或者抽象类,两者都不能直接被实例化;【细节】就是实现类,实现接口或继承抽象类而产生的类就是细节,可以被实例化
- 依赖倒置的原则的本质就是通过【抽象(接口或者抽象类)】使各个模块或类的实现彼此独立,不互相影响,实现模块间的松耦合。
example
本程序反映了 “顾客类”与“商店类”的关系。商店类中有 sell() 方法,顾客类通过该方法购物以下代码定义了顾客Customer通过 TodaySupermarket 购买商品:
/**
* 顾客类,购买商品
*/
class Customer {
void shopping(TodaySupermarket Store) {
Store.sel();
}
}
/**
* Todayc超市,出售商品
*/
class TodaySupermarket {
void sel(){
System.out.println(TodaySupermarket.class.getName()+"begin sell tomato....");
}
}
class DipTest{
public void main(String [] args){
Customer customer = new Customer();
customer.shopping(new TodaySupermarket());
}
}
但是,这种设计存在这很大的缺陷,如果顾客想去宜家购买商品,就需要修改顾客类的代码
/**
* 顾客类,购买商品
*/
class Customer {
void shopping(IKEASupermarket Store) {
Store.sel();
}
}
/**
* 宜家超市,出售商品
*/
class IKEASupermarket {
void sel(){
System.out.println(IKEASupermarket.class.getName()+"begin sell potato....");
}
}
每次更换超市都必须修改顾客类得代码,这违背了【开闭原则】,照成这种情况得原因在于:Customer在设计时依赖了具体的超市,违背了【依赖倒置原则】,对上述代进行优化
/**
* 超市
*/
interface Supermarket {
void sell();
}
接口只是一个抽象化的概念,是对一类事物的最抽象描述,具体的实现代码由相应的实现类来完成Supermarket的实现具体如下:
/**
* 宜家超市
*/
class IKEASupermarket implements Supermarket {
@Override
public void sell() {
System.out.println(IKEASupermarket.class.getName() + "begin sell potato....");
}
}
/**
* Today超市
*/
class TodaySupermarket implements Supermarket{
@Override
public void sell() {
System.out.println(IKEASupermarket.class.getName() + "begin sell Tomato....");
}
}
顾客类的实现代码如下
interface ICustomer {
void shopping(Supermarket Store);
}
class Customer implements ICustomer {
@Override
void shopping(IKEASupermarket Store) {
Store.sel();
}
}
在业务场景中,我们贯彻“抽象不应该依赖细节”,也就是我们认为抽象(Supermarket接口)不依赖Today和IKEA两个实现类(细节),因此我们在高层次的模块中应用都是抽象
class Client {
public void main(String [] args){
ICustomer li = new Customer();
Supermarket today = new TodaySupermarket();
li.shopping(today);
}
}
Client 属于高层业务逻辑,它对低层模块的依赖都建立在抽象之上,li显示的类型是ICustomer,today显示的类型是Supermarket,是一个接口,抽象的,非具体的,在其后的所有操作中today都是以Supermarket类型进行操作,屏蔽了细节对抽象的影响。当然,现在想去宜家购物也很容易,只需要修改业务场景的类就可以
class Client {
public void main(String [] args){
ICustomer li = new Customer();
Supermarket iKEA = new IKEASupermarket();
li.shopping(iKEA);
}
}
在新增低层模块时,只修改了业务类的场景,也就是高层模块,对其他低层模块如 Customer不需要进行任何修改,业务就可以运行,把 “变更” 风险降至最低
【在Java中,只要定义变量就必然要有类型,一个变量可以有两个类型:显示类型和真实类型,显示类型是在定义的时候赋予的类型,真实类型是对象的类型,如iKEA的显示类型是Supermarket,真实类型是IKEASupermarket】
依赖的三种写法
- 构造函数传递依赖对象 【在类中通过构造函数声明依赖对象,按照依赖注入的说法这种注入方式叫做 [构造函数注入]】
interface ICustomer {
void shopping(Supermarket store);
}
class Customer implements ICustomer {
private Supermarket store;
public Customer(Supermarket iStore){
this.store = iStore;
}
@Override
void shopping(IKEASupermarket store) {
this.store.sel();
}
}
- Setter方法传递依赖对象【在抽象中设置setter方法声明依赖关系,依照依赖注入的说法就是setter依赖注入】
interface ICustomer {
void shopping(Supermarket store);
}
class Customer implements ICustomer {
private Supermarket store;
public void setStore(Supermarket store){
this.store = store;
}
@Override
void shopping(IKEASupermarket store) {
this.store.sel();
}
}
- 接口声明依赖对象【 在接口的方法中声明依赖对象叫做接口注入】
interface ICustomer {
void shopping(Supermarket Store);
}
class Customer implements ICustomer {
@Override
void shopping(IKEASupermarket Store) {
Store.sel();
}
}