依赖倒置、控制反转、依赖注入、面向接口编程的理解

我的另一篇文章 :《以面向对象的角度分析抽象类和接口

依赖倒置

依赖倒置原则(Dependence Inversion Principle,DIP)是指设计代码结构时,高层模块不应该依赖低层模块,二者都应该依赖其抽象。

通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并且能够降低修改程序所造成的风险。

比如,我们有一个TeaRoom类(茶室),有drinkBlackTea 喝红茶与drinkGreenTea 喝绿茶两个方法,来模拟去茶室喝茶

代码如下(完整例子放在github上了

public class TeaRoom {

    public void drinkBlackTea() {
        System.out.println("喝"+new BlackTea().aboutTea());
    }

    public void drinkGreenTea() {
        System.out.println("喝"+new GreenTea().aboutTea());
    }
}

public class BlackTea {
  
    public String aboutTea() {
        return "红茶";
    }
}
public class ExampleUnitTest {
    @Test
    public void main() {
        /**
         * 常规实现,茶馆与茶叶耦合度太高,只能调用TeaRoom中提供的drinkBlackTea和drinkGreenTea
         * 要是想点其它茶叶,需要修改TeaRoom的代码
         */
        TeaRoom teaRoom = new TeaRoom();
        teaRoom.drinkBlackTea();
        teaRoom.drinkGreenTea();

    }
}

常规实现,茶馆与茶叶耦合度太高,只能调用TeaRoom中提供的drinkBlackTea和drinkGreenTea
要是想点其它茶叶,需要修改TeaRoom的代码。

利用依赖倒置原则修改一下

public interface ITea {
    String aboutTea();
}

/**
 * 使用依赖倒置原则解耦
 */
public class TeaRoom2 {

    private final IFeel mFeel;

    public TeaRoom2(IFeel feel) {
        this.mFeel = feel;
    }

    public void drinkTea(ITea iTea) { //依赖注入
        String tea = iTea.aboutTea();
        System.out.println("喝了" + tea);

        mFeel.feel(tea + "非常好喝");
    }

    interface IFeel {
        void feel(String feel);
    }
}
public class ExampleUnitTest {
    @Test
    public void main() {
        /**
         * 常规实现,茶馆与茶叶耦合度太高,只能调用TeaRoom中提供的drinkBlackTea和drinkGreenTea
         * 要是想点其它茶叶,需要修改TeaRoom的代码
         */
        TeaRoom teaRoom = new TeaRoom();
        teaRoom.drinkBlackTea();
        teaRoom.drinkGreenTea();

        /**
         * 依赖倒置解耦
         *
         * 高层模块不依赖底层模块,他们都应该依赖抽象
         * 解释:这里相当于高层模块,底层模块是TeaRoom(茶馆),我们去茶馆喝茶,应该是我们点什么茶上什么茶(茶是抽象,只能是茶不能点其它不是茶的),
         * 不依赖茶馆的具体实现,茶馆就是提供喝茶的,具体上什么茶取决于点什么茶,只要点的是茶(抽象),就行
         * 接口 ITea 是茶的抽象,高层和底层都是依赖该抽象的
         */
        TeaRoom2 teaRoom2 = new TeaRoom2(new TeaRoom2.IFeel() {
            @Override
            public void feel(String feel) {
                /**
                 * 控制反转
                 *
                 * 比方说去茶馆喝茶,这个主动权是在调用方,也就是我们这里
                 *
                 * 而茶叶好不好喝跟泡茶的方式或者手法是有很大关系的(只考虑客观因素),喝茶的感受跟茶馆有很大的关系
                 * 也就是说感受好与坏,主动权在茶馆,
                 *
                 * 主动权在被调用方就是控制反转,体现在程序上就是 发布/订阅
                 */
                System.out.println(feel);
            }
        });
        /**
         * 依赖注入
         *
         * 通过注入的方式获得依赖
         * A对象依赖于B对象,等价于A对象内部存在对B对象的“调用”,而前提是A对象内部拿到了B对象的引用
         * B对象的引用的来源无非有以下几种:A对象内部创建(无论是作为字段还是作为临时变量)、构造器注入、属性注入、方法注入
         */
        teaRoom2.drinkTea(new BlackTea());
        teaRoom2.drinkTea(new JasmineTea());
        teaRoom2.drinkTea(new GreenTea());
    }
}

运行结果
在这里插入图片描述

控制反转

当存在依赖倒置的时候往往也存在着控制反转
控制反转跟依赖倒置都是一种编程思想,依赖倒置着眼于调用的形式,而控制反转则着眼于程序流程的控制权
比方说去茶馆喝茶,这个主动权是在调用方,也就是我们这里
而茶叶好不好喝跟泡茶的方式或者手法是有很大关系的(只考虑客观因素),喝茶的感受跟茶馆有很大的关系
也就是说感受好与坏,主动权在茶馆,
主动权在被调用方就是控制反转,体现在程序上就是 发布/订阅,就如例子中的TeaRoom2.IFeel()回调

依赖注入

通过注入的方式获得依赖
A对象依赖于B对象,等价于A对象内部存在对B对象的“调用”,而前提是A对象内部拿到了B对象的引用
B对象的引用的来源无非有以下几种:A对象内部创建(无论是作为字段还是作为临时变量)、构造器注入、属性注入、方法注入
就如例子中的teaRoom2.drinkTea(new BlackTea());

面向接口编程

无论是依赖倒置、控制反转、还是依赖注入,都已经蕴含着“面向接口编程”的思想。面向接口,就意味着面向抽象。作为哲学范畴而言,规定性少称为抽象,规定性多称为具体。而接口,就是程序中的一种典型的“抽象”的形式。

我们对理想的编程通常概括为“高内聚,低耦合”,

内聚指的是专人专职,专门的类做专门的事;
在面向对象编程中,对象自身是内聚的,是保管好自己的数据,完成好自己的操作的,而对外界呈现出自己的状态和行为。一个对象往往不能干所有的事,都要与其它对象产生联系,一个对象和另一个对象产生依赖关系,也就是耦合。

面向对象编程其实更偏重于高内聚,面向接口更体现在低耦合
面向接口编程,目的是解耦,保证编程的健壮性、灵活性和可维护性

参考
那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值