面向对象编程原则(06)——依赖倒转原则

本文详细介绍了依赖倒转原则,该原则强调高层模块依赖抽象,而非具体实现,以提高系统的灵活性和可扩展性。通过CRM系统数据转换和电脑组装两个案例,展示了如何遵循该原则进行设计,避免代码紧耦合,实现开闭原则。重构后的代码使用接口和抽象类,实现了依赖注入,提高了代码的可维护性和可测试性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


版权声明

  • 本文原创作者:谷哥的小弟
  • 作者博客地址:http://blog.csdn.net/lfdfhl

参考资料

  1. 《大话设计模式》 作者:程杰
  2. 《Java设计模式》 作者:刘伟
  3. 《图解设计模式》 作者:结城浩
  4. 《重学Java设计模式》 作者:付政委
  5. 《Head First设计模式》作者:埃里克·弗里曼

依赖倒转原则概述

依赖倒转原则(Dependence Inversion Principle,DIP)是RobertCMartin在1996年为C++Reporter所写的专栏EngineeringNotebook的第三篇,后来加入到他在2002年出版的经典著作《Agile Software Development,Principles,Patterns,and Practices》一书中。如果说开闭原则是面向对象设计的目标,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现。

该原则定义如下:

高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

High level modules should not depend upon low level modulesboth should depend upon abstractions. Abstractions should not depend upon detailsdetails
should depend upon abstractions.

简单来说:Program to an interface not an implementation;依赖倒转原则要求面向接口编程,而不要针对实现编程。

依赖倒转原则要求在程序代码中传递参数时或在关联关系中尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

在引人抽象层后系统将具有很好的灵活性。在程序中尽量使用抽象层进行编程而将具体类写在配置文件中。这样如果系统行为发生变化,只需要对抽象层进行扩展,并修改配詈文件而无须修改原有系统的源代码。在不修改的情况下来扩展系统的功能并满足开闭原则的要求。

在实现依赖倒转原则时需要针对抽象层编程而将具体类的对象通过依赖注入(Dependence Injection;DI)的方式注人到其它对象中。依赖注人是指当一个对象要与其他对象发生依赖关系时采用抽象的形式来注人所依赖的对象。常用的注人方式有3种,分别是构造注入、设值注入(Setter注入)和接口注入。构造注入是指通过构造函数来传入具体类的对象;设值注入是指通过Setter方法来传入具体类的对象;而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象由子类对象来覆盖父类对象。

依赖倒转原则案例1

在此,以案例形式介绍依赖倒转原则。

软件公司开发人员在开发CRM系统时发现:该系统经常需要将存储在TXT或Excel文件中的客户信息转存到数据库中,因此需要进行数据格式转换。在客户数据操作类CustomerDAO中将调用数据格式转换类的方法来实现格式转换,设计方案结构如下:

在这里插入图片描述

初步实践之后发现方案存在一个非常严重的问题:由于每次转换数据时数据来源不一定相同,因此需要经常更换数据转换类。例如,有时候需要将TXTDataConvertor改为ExcelDataConvertor;一旦改调用类,此时需要就修改CustomerDAO的源代码。除此以外,在引入并使用新的数据转换类时也不得不修改CustomerDAO的源代码。由此而来,系统扩展性较差违反了开闭原则,重构如下:

  • 1、创建抽象类DataConvertor
  • 2、TXTDataConvertor和ExcelDataConvertor继承自DataConvertor
  • 3、CustomerDAO类中的引用为DataConvertor
  • 4、CustomerDAO类中的具体类型读取自配置文件config.xml

在这里插入图片描述

在上述重构过程中同时使用了开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下这3个设计原则会同时出现:开闭原则是目标,里氏替换原则是基础,依赖倒转原则是手段;它们相辅相成、相互补充、目标一致;只是分析问题时所站的角度不同而已。

依赖倒转原则案例2

在本例中,我们通过电脑的组装过程来体会依赖倒转原则。假设电脑的组装需要英特尔芯片,希捷硬盘,金士顿内存等器件。

版本1

在这里插入图片描述

IntelCpu

package com.dip01;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class IntelCpu {
    public void run() {
        System.out.println("Intel处理器在运行");
    }
}

在这里插入图片描述

KingstonMemory

package com.dip01;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class KingstonMemory {
	private String data;
	// 金士顿内存存数据
    public void save(String data) {
    	this.data = data;
        System.out.println("使用金士顿内存存储数据,data=" + data);
    }

	// 金士顿内存读数据
    public String get() {
    	String data = this.data;
        System.out.println("获取金士顿内存中的数据,data=" + data);
        return data;
    }
}

在这里插入图片描述

XiJieHardDisk

package com.dip01;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class XiJieHardDisk {
	private String data;

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

	// 希捷硬盘读数据
    public String get() {
    	String data = this.data;
        System.out.println("获取希捷硬盘中的数据,data=" + data);
        return data;
    }
}

在这里插入图片描述

Computer

package com.dip01;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Computer {
    private XiJieHardDisk hardDisk;
    private IntelCpu cpu;
    private KingstonMemory memory;
    
    public Computer() {
		
	}
    
	public Computer(XiJieHardDisk hardDisk, IntelCpu cpu, KingstonMemory memory) {
		this.hardDisk = hardDisk;
		this.cpu = cpu;
		this.memory = memory;
	}



	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;
    }

    public void run() {
        System.out.println("计算机开始工作");
        hardDisk.save("Hello World");
        hardDisk.get();
        cpu.run();
        memory.save("10101010");
        memory.get();
    }
}

在这里插入图片描述

Test

package com.dip01;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {
    public static void main(String[] args) {
    	// 创建各个配件
        XiJieHardDisk hardDisk = new XiJieHardDisk();
        IntelCpu cpu = new IntelCpu();
        KingstonMemory memory = new KingstonMemory();
        
        // 组装计算机
        Computer computer = new Computer();
        computer.setCpu(cpu);
        computer.setHardDisk(hardDisk);
        computer.setMemory(memory);

        // 启动计算机
        computer.run();
    }
}

在这里插入图片描述

测试结果:

在这里插入图片描述

小结

在该示例中,电脑的组装只能依靠特定品牌的组件,例如:英特尔芯片,希捷硬盘,金士顿内存等等。假若,换做其它芯片、硬盘、内存条就不再兼容了。所以,该示例依赖于与具体而不依赖于抽象,这明显违背了依赖倒转原则。

版本2

在这里插入图片描述

Cpu

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface Cpu {
    void run();
}

在这里插入图片描述

IntelCpu

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class IntelCpu implements Cpu{
	// 实现接口中的方法
	@Override
    public void run() {
        System.out.println("Intel处理器在运行");
    }
}

在这里插入图片描述

Memory

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface Memory {
	void save(String data);
	String get();
}

在这里插入图片描述

KingstonMemory

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class KingstonMemory implements Memory{
	private String data;

	// 实现接口中的方法
	// 金士顿内存存数据
	@Override
    public void save(String data) {
    	this.data = data;
        System.out.println("使用金士顿内存存储数据,data=" + data);
    }

	// 实现接口中的方法
	// 金士顿内存读数据
	@Override
    public String get() {
    	String data = this.data;
        System.out.println("获取金士顿内存中的数据,data=" + data);
        return data;
    }
}

在这里插入图片描述

HardDisk

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface HardDisk {
	void save(String data);
	String get();
}

在这里插入图片描述

XiJieHardDisk

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class XiJieHardDisk implements HardDisk{
	private String data;

	// 实现接口中的方法
	// 希捷硬盘存数据
	@Override
    public void save(String data) {
    	this.data = data;
        System.out.println("使用希捷硬盘存储数据,data=" + data);
    }

	// 实现接口中的方法
	// 希捷硬盘读数据
	@Override
    public String get() {
    	String data = this.data;
        System.out.println("获取希捷硬盘中的数据,data=" + data);
        return data;
    }
}

在这里插入图片描述

Computer

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Computer {
    private HardDisk hardDisk;
    private Cpu cpu;
    private Memory memory;
    
    public Computer() {
		
	}
    
	public Computer(XiJieHardDisk hardDisk, IntelCpu cpu, KingstonMemory memory) {
		this.hardDisk = hardDisk;
		this.cpu = cpu;
		this.memory = memory;
	}



	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;
    }

    public void run() {
        System.out.println("计算机开始工作");
        hardDisk.save("Hello World");
        hardDisk.get();
        cpu.run();
        memory.save("10101010");
        memory.get();
    }
}

在这里插入图片描述

Test

package com.dip02;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {
    public static void main(String[] args) {
    	// 创建各个配件
        HardDisk hardDisk = new XiJieHardDisk();
        Cpu cpu = new IntelCpu();
        Memory memory = new KingstonMemory();
        
        // 组装计算机
        Computer computer = new Computer();
        computer.setCpu(cpu);
        computer.setHardDisk(hardDisk);
        computer.setMemory(memory);

        // 启动计算机
        computer.run();
    }
}

在这里插入图片描述
测试结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷哥的小弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值