4、依赖倒置原则(Dependence Inversion Principle)

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

  1. 每一个逻辑的实现都是原子逻辑组成的,不可分割的原子逻辑就是低层模块(一般是接口、抽象类),原子逻辑的组装就是高层模块
    【高层模块】 : 调用别的方法的模块
    【低层模块】:被其他方法调用的模块
  2. 在Java语言中,【抽象】就是指接口或者抽象类,两者都不能直接被实例化;【细节】就是实现类,实现接口或继承抽象类而产生的类就是细节,可以被实例化
  3. 依赖倒置的原则的本质就是通过【抽象(接口或者抽象类)】使各个模块或类的实现彼此独立,不互相影响,实现模块间的松耦合。

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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值