Java设计模式-软件设计模式-依赖倒置原则

岳庆锦

发布于 2022.03.06 21:05 阅读 1621 评论 0

依赖倒置原则

 定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象(接口或抽象类);抽象不应该依赖细节(具体的实现类),细节应该依赖抽象。

 核心思想:要面向接口编程,不要面向实现编程。

 依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合。

 由于在软件设计中,细节具有多变性,而抽象层则相对稳定。因此,以抽象为基础搭建起来的架构,要比以细节为基础搭建起来的架构稳定得多。

 使用接口或者抽象类的目的是制定好规范和约束,而不去涉及任何具体的操作,把实现细节的任务交给它们的实现类去完成。

 实现方法:依赖倒置原则的目的是:通过面向接口的编程来降低类之间的耦合性。

 所以,我们在实际编程中遵循以下4点,就能在项目中满足这个规则。

  1.每个类尽量提供接口或抽象类,或者两者都具备。

  2.变量的声明类型尽量是接口或者抽象类。(都依赖抽象)

  3.任何类都不应该从具体类派生。

  4.使用继承尽量遵循里氏替换原则。

 应用:下面通过“组装电脑”案例,来体会依赖倒置原则。

 分析:现要组装一台电脑,需要配件cpu,硬盘,内存条(还有其它组件,只是拿这三个举例子)。只有这些配置都有了,计算机才能正常运行。cpu有很多选择,如Intel,AMD等,硬盘可以选择捷西,西数等,内存条可以选择金士顿,海盗船等。

1.捷西硬盘类(XiJieHardDisk),包含存储数据和读取数据的方法。

public class XiJieHardDisk{

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

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

2.Intel处理器类(IntelCpu),包含运行CPU的方法。

public class IntelCpu {

    //运行CPU
    public void run() {
        System.out.println("使用Intel处理器");
    }
}

3.金士顿内存条类(KingstonMemory),包含存储数据的方法。

public class KingstonMemory {

    //存储数据
    public void save() {
        System.out.println("使用金士顿内存条");
    }
}

4.电脑类(Computer)

public class Computer {

    //组装电脑需要组件,所以聚合不同组件类对应的对象
    private XiJieHardDisk hardDisk;
    private IntelCpu cpu;
    private KingstonMemory memory;

    /**
     * 运行电脑
     */
    public void run(){
        System.out.println("运行计算机");
        //开机会先加载硬盘,获取数据
        String data = hardDisk.get();
        System.out.println("从硬盘上获取的数据是:"+data);
        cpu.run();
        memory.save();
    }

    //get和set方法(set方法用于组装计算机)
    public XiJieHardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(XiJieHardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public IntelCpu getCpu() {
        return cpu;
    }

    public void setCpu(IntelCpu cpu) {
        this.cpu = cpu;
    }

    public KingstonMemory getMemory() {
        return memory;
    }

    public void setMemory(KingstonMemory memory) {
        this.memory = memory;
    }
}

5.测试类(ComputerDemo)

public class ComputerDemo {
    public static void main(String[] args) {
        //1,创建计算机对象(此时计算机还只是一个空壳)
        Computer c = new Computer();

        //2,创建计算机组件
        XiJieHardDisk hardDisk = new XiJieHardDisk();
        IntelCpu cpu = new IntelCpu();
        KingstonMemory memory = new KingstonMemory();

        //3,组装计算机
        c.setCpu(cpu);
        c.setHardDisk(hardDisk);
        c.setMemory(memory);

        //4,运行计算机
        c.run();
    }
}

6.运行结果

 运行结果分析:上面的代码已经实现了组装一台电脑,但是组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的。但如果用户想按照自己的喜好选择自己喜欢的配件,就需要修改原来的代码,这就违背了“开闭原则”

 举个栗子,假如用户想要用AMD的处理器(CPU),就需要修改Computer类中的代码:

public class Computer {

    //组装电脑需要组件,所以聚合不同组件类对应的对象
    private XiJieHardDisk hardDisk;
    private AMDCpu cpu;
    private KingstonMemory memory;

    /**
     * 运行电脑
     */
    public void run(){
        System.out.println("运行计算机");
        //开机会先加载硬盘,获取数据
        String data = hardDisk.get();
        System.out.println("从硬盘上获取的数据是:"+data);
        cpu.run();
        memory.save();
    }

    //get和set方法(用于组装计算机)
    public XiJieHardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(XiJieHardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public AMDCpu getCpu() {
        return cpu;
    }

    public void setCpu(AMDCpu cpu) {
        this.cpu = cpu;
    }

    public KingstonMemory getMemory() {
        return memory;
    }

    public void setMemory(KingstonMemory memory) {
        this.memory = memory;
    }
}

 存在以上缺点的原因:计算机类设计时同具体的组件类(XiJieHardDisk、IntelCpu、KingstonMemory)绑定了,这违背了“依赖倒置原则”

 改进方法:把希捷硬盘向上抽取出一个父接口HardDisk,Intel处理器向上抽取出一个父接口CPU,金士顿内存条向上抽取出一个父接口Memory。Computer类依赖接口面向接口编程。这样一来,不管用户想用什么组件组装计算机(Computer),都不需要修改原有的代码,符合“开闭原则”。

 改进后代码

1.硬盘接口(HardDisk)

public interface HardDisk {
    /**
     * 存储数据
     * @param data
     */
    public void save(String data);

    /**
     * 获取数据
     * @return
     */
    public String get();
}

1.1希捷硬盘(XiJieHardDisk)

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

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

2.CPU接口(CPU)

public interface Cpu {
    /**
     * 运行cpu
     */
    public void run();
}

2.1Intel处理器(IntelCpu)

public class IntelCpu implements Cpu {
    @Override
    public void run() {
        System.out.println("使用Intel处理器");
    }
}

3.内存条接口(Memory)

public interface Memory {
    /**
     * 内存条存储数据
     */
    public void save();
}

3.1金士顿内存条(KingstonMemory)

public class KingstonMemory implements Memory {
    @Override
    public void save() {
        System.out.println("使用金士顿内存条");
    }
}

4.计算机类(Computer),依赖接口

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.run();
        memory.save();
    }

    public HardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(HardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public Cpu getCpu() {
        return cpu;
    }

    public void setCpu(Cpu cpu) {
        this.cpu = cpu;
    }

    public Memory getMemory() {
        return memory;
    }

    public void setMemory(Memory memory) {
        this.memory = memory;
    }
}

5.测试类

public class ComputerDemo {
    public static void main(String[] args) {
        //1,创建计算机对象
        Computer c = new Computer();

        //2,创建计算机组件
        HardDisk hardDisk = new XiJieHardDisk();
        Cpu cpu = new IntelCpu();
        Memory memory = new KingstonMemory();

        //3,组装计算机
        c.setCpu(cpu);
        c.setHardDisk(hardDisk);
        c.setMemory(memory);

        //4,运行计算机
        c.run();
    }
}

6.运行结果