什么是IOC

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 什么是IOC

工作好多年了, 最近想要回过头来,好好看看spring源码, 但是发现有些基础还是要在在巩固一下, 那么这次就从ioc看起吧. 相信每次回看,都有会不一样的收获

我们知道spring的思想主要是ioc, 但本篇文章, 暂且先不说spring, 只谈ioc

参考了一篇文章, 讲解ioc, 非常不错, 简单易懂, 描述了ioc到底是什么 https://blog.csdn.net/ivan820819/article/details/79744797


IOC的中心思想, 用下图描述

1187916-20210213064119082-1236595609.png


一、IOC理论部分



1.IOC的理论背景


我们知道在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统地业务逻辑[1]。

1187916-20210214042302923-1580579052.png

图1 软件系统中耦合的对象


  如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒针顺时针旋转,从而在表盘上产生正确的时间。图1中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。


  齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似。对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

30131157-f547678cffaf421b89373e3bb308757d.png


 图2 对象之间的依赖关系


  耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson 1996年提出了IOC理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实践当中。


2.什么是IOC


  IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。


  1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。


  IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦。如下图

1187916-20210214044128311-348097065.png


图3 IOC解耦过程


  大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。


  我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:

1187916-20210214044215349-410483112.png


图4 拿掉IOC容器后的系统


  我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!


   我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:

   软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。


   软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。


   通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。


3.IOC也叫依赖注入(DI)


  2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

  所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。


  学过IOC的人可能都看过Martin Fowler(老马,2004年post)的这篇文章:Inversion of Control Containers and the Dependency Injection pattern[2]。


  博客园的园友EagleFish(邢瑜琨)的文章: 深度理解依赖注入(Dependence Injection)[3]对老马那篇经典文章进行了解读。


   CSDN黄忠成的Inside ObjectBuilder[4]也是,不过他应该来自台湾省,用的是繁体,看不管繁体中文的,可以看园中的吕震宇博友的简体中文版[转]Object Builder Application Block[5] 。


4.IOC的优缺点


In my experience, IoC using the Spring container brought the following advantages[6]:

  • flexibility
  • changing the implementation class for a widely used interface is simpler (e.g. replace a mock web service by the production instance)
  • changing the retrieval strategy for a given class is simpler (e.g. moving a service from the classpath to the JNDI tree)
  • adding interceptors is easy and done in a single place (e.g. adding a caching interceptor to a JDBC-based DAO)
  • readability
  • the project has one unified and consistent component model and is not littered with factories (e.g. DAO factories)
  • the code is briefer and is not littered without dependency lookup code (e.g. calls to JNDI InitialContext)
  • testability
  • dependencies are easy to replace mocks when they're exposed through a constructor or setter
  • easier testing leads to more testing
  • more testing leads to better code quality, lower coupling, higher cohesion

  使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点,做到心中有数,杜绝滥用框架[1]。


   第一、软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。


   第二、由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。


   第三、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。


   第四、IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。


   我们大体可以得出这样的结论:一些工作量不大的项目或者产品,不太适合使用IOC框架产品。另外,如果团队成员的知识能力欠缺,对于IOC框架产品缺乏深入的理解,也不要贸然引入。最后,特别强调运行效率的项目或者产品,也不太适合引入IOC框架产品,像WEB2.0网站就是这种情况。


5.IOC容器的技术剖析


  IOC中最基本的技术就是“反射(Reflection)”编程,目前.Net C#、Java和PHP5等语言均支持,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。反射的应用是很广泛的,很多的成熟的框架,比如象Java中的Hibernate、Spring框架,.Net中 NHibernate、Spring.Net框架都是把“反射”做为最基本的技术手段。


6.IOC容器的一些产品


  Sun ONE技术体系下的IOC容器有:轻量级的有Spring、Guice、Pico Container、Avalon、HiveMind;重量级的有EJB;不轻不重的有JBoss,Jdon等等。Spring框架作为Java开发中SSH(Struts、Spring、Hibernate)三剑客之一,大中小项目中都有使用,非常成熟,应用广泛,EJB在关键性的工业级项目中也被使用,比如某些电信业务。


   .Net技术体系下的IOC容器有:Spring.Net、Castle等等。Spring.Net是从Java的Spring移植过来的IOC容器,Castle的IOC容器就是Windsor部分。它们均是轻量级的框架,比较成熟,其中Spring.Net已经被广泛应用于各种项目中。


  总之就是很多很多,不甚枚举.....

 

二、实战分析



上面说了ioc的由来, 以及什么是ioc, 那面我们来看看实际项目中的应用

通常, 为了解耦, 我们会使用三层模式, 即: controller, service, dao. 其中dao层进行是数据库访问.

 

假设: 我们最开始dao层连接的是oracle数据库. service层调用dao层.

public interface UserOracleDao {
    void add(String name, String age);
}
public class UserOracelDaoImpl implements UserOracleDao {
    @Override
    public void add(String name, String age) {
        System.out.println("调用oracle数据库, 添加user");
    }
}

service调用dao

public interface UseService {
    void add(String name, String age);
}
public class UserServiceImpl implements UseService {
    @Override
    public void add(String name, String age) {
        UserOracleDao userOracleDao = new UserOracelDaoImpl();
        userOracleDao.add(name, age);
    }
    public static void main(String[] args) {
        UseService useService = new UserServiceImpl();
        useService.add("zs", "12");
    }
}

这是我们最开始mvc方式的写法, 在引入dao的时候, 会强依赖UserOracleDao及其实现类.

可是, 如果有一天, 我们要做数据库升级, 感觉mysql数据库会更好. 于是将所有的Oracle数据库都替换为mysql数据库.

如上, 我们会怎么做呢?

首先, 引入mysql数据源

第二, 新增或者修改dao层的调用

public interface UserMysqlDao {
    void add(String name, String age);
}
public class UserMysqlDaoImpl implements UserMysqlDao {
    @Override
    public void add(String name, String age) {
        System.out.println("调用mysql数据库, 添加user");
    }
}

第三, 修改对dao层的引用为mysql数据库

public class UserServiceImpl implements UseService {
    @Override
    public void add(String name, String age) {
        //UserOracleDao userDao = new UserOracelDaoImpl();
        UserMysqlDao userDao = new UserMysqlDaoImpl();
        userDao.add(name, age);
    }
    public static void main(String[] args) {
        UseService useService = new UserServiceImpl();
        useService.add("zs", "12");
    }
}


我们发现, 这样一来,修改了三处地方. 首先, 增加了链接mysql数据库操作数据的dao接口, 然后, 修改了dao实现类. 第三步:修改service调用的dao实现.

我们看到, 目前只有一个user信息, 要想从oracle替换为mysql, 需要三大步, 而实际项目中, 有许许多多这样的类, 如果有100个, 那么就要修改100*3个地方. 这太恐怖了

而且, 我们都知道, 代码的修改,没修改一步, 就可能产生一定的异常风险. 修改的越多, 风险越大.

这就是因为, service层和dao强耦合在一起. 在service层直接调用了dao的实现类.

如果,下次替换为mysql,那么service中引用dao的地方,就要替换为操作mysql数据库的dao.

那么有没有办法,让他们几个好好的玩一玩呢? IOC就是这样的一个理念. 减少模块间的相互依赖.

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
5月前
|
设计模式 Java 容器
控制反转 (IoC)
【8月更文挑战第24天】
46 0
|
8月前
|
容器
IOC 控制反转和DI依赖注入
IOC 控制反转和DI依赖注入
|
Java Maven
SpringFrame-ioc 依赖注入
SpringFrame-ioc 依赖注入
|
JavaScript uml 容器
Ioc——控制反转
Ioc——控制反转
217 0
Ioc——控制反转
|
Java API C++
IOC理解
成功就是简单道理的深刻理解与灵活运用 前不久,阿里大牛虾总在群里抛出一个问题:“从深层次讲解一下如何理解IOC,IOC和DI是一回事吗?” 这个问题真是让人平静而又不平静 平静源于此问题就像问中国人怎么使用筷子,天天使用筷子,难道还不会使用筷子? 但又不平静,你能写出一份详细的说明书,让一个不会使用筷子的人按此说明成功地使用上筷子吗?
360 0
IOC理解
|
自动驾驶 小程序 Java
什么是控制反转(IOC)?什么是依赖注入?
什么是控制反转(IOC)?什么是依赖注入?
什么是控制反转(IOC)?什么是依赖注入?
|
XML 存储 Java
|
Java 程序员 数据库
依赖注入IOC
依赖注入IOC
|
Java Spring
Ioc个人理解
IOC根据官方给的意思是依赖注入,说白了,调用类的方面不用"new 类名",而是在配置文件中配置后,然后新建一个工厂类,这样在工厂类中就可以直接调用了。下面是实现IOC的过程。
156 0
Ioc个人理解
|
Java 编译器 容器
IoC与AOP的那点事儿
IoC与AOP的那点事儿
112 0

热门文章

最新文章