3. 依赖倒转原则

三、 依赖倒转原则

1. 概念

高层模块不应该依赖低层模块,应该依赖其抽象;抽象不应该依赖细节(具体的实现类或者子类),细节应该依赖抽象。简单的说就是要求对抽象进行编程不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

我第一次看到这个概念的时候,一脸懵逼。下面让我们通过几幅图来看看,加深理解。

先是:高层模块不应该依赖低层模块 这句话,什么是高层模块,什么是低层模块。

这里拓展下UML类图的知识点,依赖关系是对象间耦合度最弱的一种关联方式,在代码中,某个类的方法通过局部变量,方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。

依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。如下图,学校里有老师上课。
高层模块&底层模块
依赖抽象:怎么理解呢?

Teacher类不是一个接口,也不是一个抽象类。而我们的School还以来了Teacher,所以我们需要将Teacher类进行向上抽取。抽取成 抽象类 或者 接口,此时我们的School再去依赖这个抽象类或者接口。这就是依赖抽象的概念

知识点补充:继承和实现关系

继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。

在 UML 类图中,继承关系用带空心三角箭头的实线来表示,箭头从子类指向父类。

在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从子类指向父类。

下面这幅图就是依赖抽象
在这里插入图片描述

依赖倒转原则是开闭原则的一种实现,大家可以想一想,开闭原则的内容:对扩展开放,对修改关闭。而我们的依赖倒转原则主要就是四个字:依赖抽象。

我们在日常开发代码中,写的方法,里面是不是经常会看到参数是接口的形式,比如我们要接收一个collection集合,但是在实现的时候是不是可以把list传进去。也可以把set传进去。

找到感觉了吗?

如果某一天我们要写一个属于自己的集合xxx,我们也可以实现collection,这样不用修改之前的方法。传入我们自己的xxx集合,这满足开闭原则,没有动原代码,增加了功能。

下面举个例子,继续帮大家找感觉

【组装电脑】

现要组装一台电脑,需要配件cpu,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿,海盗船等。

首先我们先写一个计算机,里面用的是希捷硬盘,Intel的cpu,金士顿的内存条去组装一台计算机。下面是他的类图。
在这里插入图片描述

代码:

/**
 * 希捷硬盘
 */
public class XiJieHardDisk {
    /**
     * 存储数据
     *
     * @param data 存储的数据
     */
    public void save(String data) {
        System.out.println("使用希捷硬盘存储数据为:" + data);
    }

    public String get() {
        System.out.println("使用希捷硬盘获取数据");
        return "数据";
    }
}

/**
 * 金士顿内存条
 */
public class KingstonMemory {
    public void save(String data) {
        System.out.println("使用金士顿内存条存储数据为:" + data);
    }

}

/**
 * Intel的处理器
 */
public class IntelCpu {
    public void calculate() {
        System.out.println("使用Intel处理器计算");
    }
}

@Data
public class Computer {
    private XiJieHardDisk xiJieHardDisk;
    private IntelCpu intelCpu;
    private KingstonMemory kingstonMemory;

    public void run() {
        System.out.println("电脑运行");
        String data = xiJieHardDisk.get();
        System.out.println("从硬盘中获取的数据是:" + data);
        intelCpu.calculate();
        kingstonMemory.save("data");
    }
}

public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setIntelCpu(new IntelCpu());
        computer.setKingstonMemory(new KingstonMemory());
        computer.setXiJieHardDisk(new XiJieHardDisk());
        computer.run();
    }
}

我们可以看得出来,我们定义的这个电脑就成功的运行起来了。但是这样写,可扩展性就很低,而且不满足开闭原则,如果我的电脑想换一种cpu呢,是不是就得修改原有的代码?

这时候,想起来四个大字:依赖抽象

我们要根据依赖倒转原则来重写,下面是新的结构类图
在这里插入图片描述
更新后的代码:


/**
 * 硬盘接口
 */
public interface HardDisk {
    String get();

    void save(String data);
}

/**
 * 希捷硬盘
 */
public class XiJieHardDisk implements HardDisk {
    /**
     * 存储数据
     *
     * @param data 存储的数据
     */
    @Override
    public void save(String data) {
        System.out.println("使用希捷硬盘存储数据为:" + data);
    }

    @Override
    public String get() {
        System.out.println("使用希捷硬盘获取数据");
        return "数据";
    }
}

/**
 * Cpu接口
 */
public interface Cpu {
    void calculate();
}


/**
 * Intel的处理器
 */
public class IntelCpu implements Cpu {
    @Override
    public void calculate() {
        System.out.println("使用Intel处理器计算");
    }
}

/**
 * AMD CPU
 */
public class AmdCpu implements Cpu {
    @Override
    public void calculate() {
        System.out.println("使用AMD处理器");
    }
}


/**
 * 内存条接口
 */
public interface Memory {
    void save(String data);
}

/**
 * 金士顿内存条
 */
public class KingstonMemory implements Memory {
    @Override
    public void save(String data) {
        System.out.println("使用金士顿内存条存储数据为:" + data);
    }

}

/**
 * 电脑实体
 */
@Data
public class Computer {
    private HardDisk hardDisk;
    private Cpu cpu;
    private Memory memory;

    public void run() {
        System.out.println("电脑运行");
        String data = hardDisk.get();
        System.out.println("从硬盘中获取的数据是:" + data);
        cpu.calculate();
        memory.save("data");
    }
}

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        //使用Intel的Cpu,希捷的硬盘,金士顿的内存条
        getComputerOne();
        System.out.println("========================");
        //使用AMD的Cpu,希捷的硬盘,金士顿的内存条
        getComputerSecond();
        System.out.println("========================");
        //使用lambda表达式(匿名内部类),前提是:接口中只有一个方法
        getComputerThree();
    }

    /**
     * 第一台计算机:感受依赖倒转原则
     */
    private static void getComputerOne() {
        Computer computer = new Computer();
        computer.setCpu(new IntelCpu());
        computer.setMemory(new KingstonMemory());
        computer.setHardDisk(new XiJieHardDisk());
        computer.run();
    }

    /**
     * 第二台计算机:感受依赖倒转原则
     */
    private static void getComputerSecond() {
        Computer computer = new Computer();
        computer.setCpu(new AmdCpu());
        computer.setMemory(new KingstonMemory());
        computer.setHardDisk(new XiJieHardDisk());
        computer.run();
    }

    /**
     * 第三台计算机:使用匿名内部类实现接口,前提是:接口中只有一个方法
     */
    private static void getComputerThree() {
        Computer computer = new Computer();
        computer.setCpu(() -> System.out.println("正在使用Intel的最新Ultra处理器来运行程序:"));
        computer.setMemory(new KingstonMemory());
        computer.setHardDisk(new XiJieHardDisk());
        computer.run();
    }
}

经过上述的例子,对依赖倒转原则和开闭原则理解的是不是又加深了!!!后面还有很多,我们慢慢来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值