多态实现依赖倒转原则

多态实现依赖倒转原则


依赖倒转原则(Dependency Inversion Principle,DIP):是指将两个模块之间的依赖关系倒置为依赖抽象类或接口。具体有两层含义:

  • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
  • 抽象不应该依赖于细节,细节应该依赖于抽象。

一、为什么要设计这种原则呢?

传统的结构化程序设计当中,高层次模块要完成自己封装的功能,就必须要使用低层模块(即继承低层模块),于是高层模块就依赖于低层模块。但低层模块经常会发生变动,一旦低层模块发生改变,高层模块也会受到影响,也需要进行修改。

在面向对象设计中,类和类之间依赖关系可以分为两种类型:

  • 具体耦合关系:发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成。
  • 抽象耦合关系:发生在一个具体类和一个抽象类(或接口)之间,使两个必须发生关系的类之间存有最大的灵活性。
    如果高层模块直接调用低层模块提供的服务,那么就是具体耦合关系,这样高层模块依赖于低层模块就不可避免。但是,如果我们使用抽象耦合关系,在高层模块和低层模块之间定义一个抽象接口,高层模块调用抽象接口定义的方法,低层模块实现该接口。这样,就消除了高层模块和低层模块之间的直接依赖关系。现在,高层模块就不依赖于低层模块了,二者都依赖于抽象。同时也实现了“抽象不应该依赖于细节,细节应该依赖于抽象”。

二、名词解析

  1. 多态:指允许不同类型的对象对同一消息做出不同的响应。即同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。也就是说,同一个接口,可以使用不同的实例而执行不同操作。
  2. 依赖:指如果一个模块A使用另一个模块B,我们称模块A依赖模块B。一般可以通过继承关系来体现。
  3. 高/低层模块:在应用程序中,有一些低层次的类,这些类实现了一些基本的或初级的操作,我们称之为低层模块;另外,有一些高层次的类,这些类封装了某些复杂的逻辑,这些类我们称之为高层模块。

三、案例分析

3.1 定义一个接口

(高层次模块需要的服务或功能的抽象描述)

// 抽象接口  
public interface AnimalSound {  
    void makeSound();  
}

3.2 创建两个实现这个接口的类

(低层次模块的具体实现)

// 猫类,实现AnimalSound接口  
public class Cat implements AnimalSound {  
    @Override  
    public void makeSound() {  
        System.out.println("喵喵喵");  
    }  
}  
  
// 狗类,实现AnimalSound接口  
public class Dog implements AnimalSound {  
    @Override  
    public void makeSound() {  
        System.out.println("汪汪汪");  
    }  
}

3.3 创建一个高层次模块

(它依赖于上述的抽象接口,而不是具体的类/低层模块)

// 高层次模块,依赖于AnimalSound接口  
public class AnimalSoundMaker {  
    private AnimalSound animalSound;  
  
    // 通过构造函数注入依赖  
    public AnimalSoundMaker(AnimalSound animalSound) {  
        this.animalSound = animalSound;  
    }  
  
    public void makeAnimalSound() {  
        animalSound.makeSound(); // 调用接口方法,实现多态  
    }  
}

3.3 在主程序中创建具体的对象,并传递给高层次模块

public class Main {  
    public static void main(String[] args) {  
        // 创建猫对象  
        AnimalSound cat = new Cat();  
        // 创建高层次模块对象,并传入猫对象  
        AnimalSoundMaker catSoundMaker = new AnimalSoundMaker(cat);  
        catSoundMaker.makeAnimalSound(); // 输出:喵喵喵  
  
        // 创建狗对象  
        AnimalSound dog = new Dog();  
        // 创建高层次模块对象,并传入狗对象  
        AnimalSoundMaker dogSoundMaker = new AnimalSoundMaker(dog);  
        dogSoundMaker.makeAnimalSound(); // 输出:汪汪汪  
    }  
}

在这个例子中,AnimalSound 是抽象接口,Cat 和 Dog 是具体的实现类,AnimalSoundMaker 是高层次模块,它依赖于 AnimalSound 接口而不是具体的 Cat 或 Dog 类。这样,我们就通过多态实现了依赖反转原则,高层次模块不再依赖于具体的实现类,而是依赖于抽象接口。这增加了代码的灵活性和可维护性,因为我们可以很容易地添加新的动物类(如 Bird),只需要在低层模块中添加新的动物类(如 Bird)即可,而不需要修改 AnimalSoundMaker 类。

四、依赖注入的方式

依赖注入(Dependency Injection,DI)是一种设计模式,可以将对象的创建和对象之间的依赖关系分离,从而使代码更加模块化、可测试和可维护。

4.1 构造函数注入(Constructor Injection)

通过将依赖关系作为构造函数参数传递来实现依赖注入。在本文中的3.3节对AnimalSoundMaker类的实现就采用了构造函数注入的方式。

4.2 Setter方法注入(Setter Injection)

通过对象的Setter方法来注入依赖。允许在对象创建后动态地注入依赖关系,灵活性较高。

public class MyClass {
    private MyDependency myDependency;
 
    // Setter方法注入
    public void setMyDependency(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

4.3 接口注入(Interface Injection)

通过对象实现一个特定的接口来注入依赖。注:接口注入较少被使用,通常情况下不如构造器注入和Setter方法注入灵活。

public interface DependencyInjector {
    void injectDependency(MyClass myClass);
}
 
public class MyClass implements DependencyInjector {
    private MyDependency myDependency;
 
    // 接口注入
    @Override
    public void injectDependency(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}
  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值