• 控制反转(IOC)/依赖注入(DI)理解


    个人学习笔记,来自Acode

    1、术语

    控制反转/反向控制,英文全称“Inversion of Control”,简称IoC。

    依赖注入,英文全称“Dependency Injection”,简称DI。

    据说是Martin Fowler对控制反转的原理进行了深入的探索后,为控制反转起了个新的名字叫“依赖注入”。也就是说,这两个术语讲的是同一个事物。

    2、控制反转的“奥义”

      “实现必须依赖抽象,而不是抽象依赖实现”。

    3、实例理解

      只看了控制反转的奥义,对于一般人来说还是难以理解,就好比一位功夫大师告诉我们“无招胜有招”,资质平平的我还是无法理解到大师们的话,于是就凌乱了...

      下面,还是通过实际的程序来理解一下控制反转。

    假设场景:从SQL Server数据库中获取数据。

    【普通方法】

    一般人会这么写程序:

    首先,实现一个对数据库进行各种操作的类SqlServerDataBase:

     1 /*
     2 *SqlServerDataBase.java
     3 **/
     4 
     5 public class SqlServerDataBase {
     6     
     7     /*
     8     *构造函数、数据库连接等操作对应的语句
     9     */
    10 
    11     //从SQL Server数据库中获取数据
    12     public List getDataFromSqlServer() {
    13         //获取数据的具体语句
    14     }
    15 }

    然后,就可以在业务层通过对象的实例来获取需要的数据:

     1 /*
     2 *Business.java
     3 **/
     4 
     5 public class Business{
     6     private SqlServerDataBase db = new SqlServerDataBase();
     7 
     8     //从SQL Server数据库中获取数据
     9     public void getData() {
    10         List list = db.getDataFromSqlServer();
    11     }
    12 
    13 }

    这么一看,感觉代码写得很不错啊,完美地满足了需求。可是,某一天,决定要将数据库更换为MySQL,于是我们历尽千辛万苦写的两个程序都不能用了,我们需要重写下面的代码:

    /*
    *MySQLDataBase.java
    **/
    
    public class MySQLDataBase {
        
        /*
        *构造函数、数据库连接等操作对应的语句
        */
    
        //从MySQL数据库中获取数据
        public List getDataFromMySQL() {
            //获取数据的具体语句
        }
    }
    
    /*
    *Business.java
    **/
    
    public class Business{
        private MySQLDataBase db = new MySQLDataBase();
    
        //从MySQL数据库中获取数据
        public void getData() {
            List list = db.getDataFromMySQL();
        }
    
    }

    我们又费劲巴拉地写了上面的两个文件。这时,客户又决定要用Oracle作为数据!!!这时,估计你的内心是无比的崩溃的。为了毛爷爷,我们忍了,再次写了新的程序。

    当写了以Oracle为数据库的程序后,客户说要用MongoDB作为数据库!!!这时,感觉再也不要当程序员了。

    总结:

      从上面的例子中可以看出,一旦客户的业务需求变了,我们写的代码基本就白搭了,还要重写新的代码。这是为什么呢?原因是:Business类的中使用到了具体的数据库对应的类,一旦要使用的数据库改变了,Business中的代码也得跟着改啊(getDataFromSqlServer()->getDataFromMySQL()->...)。

    教训:

      要实现一种代码可以多次重复利用,不依赖于具体业务的代码,实现Business类的重用。这时,我们想到了大神告诉我们的控制反转的奥义“实现必须依赖抽象,而不是抽象依赖实现”。

    【IoC方式实现】

    1、首先,定义一个从数据库中获取数据的接口DataBase,作为一个接口,它里面只有方法的声明,此处我们需要一个从数据库中获取数据的方法:getData()。

     1 /*
     2 *DataBase.java
     3 */
     4 
     5 public interface DataBase{
     6     //该方法用来获取数据
     7     public void getData();
     8 }
    

    2、接着,此时客户要求采用SQL Server作为项目的数据库,那么就实现一个负责从SQL Server数据库中获取数据的类SqlServerDataBase:

     1 /*
     2 *SqlServerDataBase.java
     3 */
     4 
     5 public class SqlServerDataBase implements DataBase{
     6     //该方法用来获取数据
     7     public void getData() {
     8         //以下是具体从SQL Server数据库中取数据的代码
     9         ......
    10     }
    11 }

    该类具体实现了从SQL Server数据库中获取数据的过程。

    3、此时,业务需要从数据库中获取数据,实现业务类Business(注意该类的写法,很重要):

     1 /*
     2 *Business.java
     3 */
     4 
     5 public class Business {
     6     //针对接口DataBase定义变量
     7     private DataBase db;
     8 
     9     public void setDataBase(DataBase db) {
    10         this.db=db;
    11     }
    12 
    13     //根据注入的数据库类,从xxx数据库中获取数据
    14     public void getData() {
    15         db.getData();
    16     }
    17 }

    注意的地方:

      (1)在第7行,定义了一个操作数据库的接口的变量db,并不是依赖于具体实现的类的变量;

      (2)定义了setDataBase()方法,该方法实现了具体实现的数据库操作类的变量的注入;

      (3)getData()方法或根据实际注入的数据库类型获取数据。

    总结:

      可以看出,该方法的实现是依赖于抽象,而非依赖于具体的实现,是不是想到了IoC的终极奥义“实现必须依赖抽象,而不是抽象依赖实现”。

    4、通过3中的Business类,实现了代码的重用(个人感觉,也就是对业务层也实现了“兼容性强的一种封装”,因此才实现了代码的重用),下面就看一下怎么根据具体的数据库类型来实现数据库的取数据:

     1 /*
     2 *TestBusiness.java
     3 */
     4 
     5 public class TestBusiness {
     6     private Business Business = new Business();
     7 
     8     //根据注入的数据库类,从SQL Server数据库中获取数据
     9     public void getData() {
    10         business.setDataBase(new SqlServerDataBase());
    11         business.getData();
    12     }
    13 }

    在第10行中,我们将SQL Server类的变量的注入到业务类Business中,也就实现了从SQL Server数据库中取数据的操作。此时,我们就再也不怕客户更换数据库了。

    假设客户要采用MySQL作为数据库,那么,我们需要做的只有两点:

    (1)实现基于MySQL的取数据的类MySQLDataBase

     1 /*
     2 *MySQLDataBase.java
     3 */
     4 
     5 public class MySQLDataBase implements DataBase{
     6     //该方法用来获取数据
     7     public void getData() {
     8         //以下是具体从MySQL数据库中取数据的代码
     9         ......
    10     }
    11 }

    (2)更改注入的数据库类型

     1 /*
     2 *TestBusiness.java
     3 */
     4 
     5 public class TestBusiness {
     6     private Business Business = new Business();
     7 
     8     //根据注入的数据库类,从MySQL数据库中获取数据
     9     public void getData() {
    10         business.setDataBase(new MySQLDataBase());
    11         business.getData();
    12     }
    13 }

    我们只需要更改第10行中药注入的数据库类型,就完成了所有的工作。

    此时,再次理解下IoC的终极奥义:实现必须依赖抽象,而不是抽象依赖实现。(修炼的境界又提升了一层)

    转载请注明出处:http://www.cnblogs.com/acode/p/5356686.html

  • 相关阅读:
    管理之道
    Swagger-editor安装启动及错误处理,注意事项
    装箱 拆箱 枚举 注解 多态
    Spring Security 内置过滤器表
    Spring Boot入门 and Spring Boot与ActiveMQ整合
    消息中间件解决方案JMS
    网页静态化解决方案-Freemarker demo+语法
    spring-data-radis错误
    java基础总结
    swift oc 混编的头文件
  • 原文地址:https://www.cnblogs.com/acode/p/5356686.html
Copyright © 2020-2023  润新知