写点什么

IoC 究竟是什么?——IoC 的基础分析

  • 2025-01-13
    福建
  • 本文字数:2765 字

    阅读完需:约 9 分钟

IoC 全称 Inversion of Control,直译为控制反转。这是一种设计理念,并非技术。在明白控制反转之前,应该知道“反转”反的是什么。


被反转的正转


我们从生活中的做饭场景开始。


在家做菜与餐馆吃饭


我们在做饭的时候有很多个步骤,需要准备原料,厨具等等。最后通过一定的顺序加入我们的原料,再进行翻炒等烹饪操作就能够得到一份菜了。我们想吃一份菜不一定需要自己做,也可以去餐馆吃,只需要告诉餐馆我们要吃什么,餐馆就能自己做好给到我们手上吃


控制的正与反


在这个做饭的例子中,正转就是我们自己准备原料,自己通过烹饪方法做菜,而控制反转就是我们去的餐馆。这个时候我相信你还是不明白这俩有什么关系,为什么正转是自己做,而反转变成了餐馆的概念。没关系,我们继续深入。


什么是反转,反转在哪?


我们再回顾一遍,我们吃到菜是个什么流程。


  • 自己做饭:想好要做的菜——自己准备原料——自己烹饪——成品

  • 餐馆:想好要吃的菜——给餐馆说——餐馆烹饪——成品


我们自己设计一整套程序的时候,往往是有很多模块的,每个模块会相互协作使用,最终形成一个大的程序,我们需要自己一个一个将模块联系起来。这就是我们自己做菜的过程而控制反转 IoC 就是将所有需要的模块通过一个容器(可以理解为一个控制终端)联系起来,我们不需要思考这个模块会不会使用其他的模块才能完成,全部都由容器帮我们完成联系。这就是我们去餐馆的过程,我们不用做菜,交给餐馆,而一个个原材料就是餐馆去准备的。最终我们能看到反转的地方就在于,我们本来是靠自己去一个一个联系其模块来,但我们全都交给了容器,容器替我们完成了联系,我们反转了对自己的依赖,本来是依赖自己去联系的,现在变依赖容器,反转就在这。换句话说,控制反转应该叫控制反转


为什么要有 IoC


这个时候你会说,这样不是省了很多事吗,省事不就是 IoC 的意义吗?对了,但没完全对。换句话说,省事了,但没完全省。因为每个依赖还是要我们自己去配置的,只不过换了种方式(这个后面再说),IoC 容器只是封装而已,但是这并不代表 IoC 没用,IoC 最大的意义不在于它更方便,而是在于它能解耦


解耦在哪?


虽然容器只是帮我们完成了每个模块的依赖,但是我们前面说到,控制反转最大的意义就是我们只用把需要的功能给到容器,容器再去思考其他的,这样的话我们再写每个模块功能的时候就会更加独立,不用考虑依赖,每个模块变得更加独立那不就是解耦的初衷吗


实例


我们最后再来个实例看看效果我们先创建一个接口


public interface Shape { 	void draw(); }
复制代码


然后实现几个个接口


//Rectangle.javapublic class Rectangle implements Shape {
@Override public void draw() { System.out.println("Inside Rectangle::draw() method."); }}
复制代码


//Square.javapublic class Square implements Shape {
@Override public void draw() { System.out.println("Inside Square::draw() method."); }}
复制代码


//Circle.javapublic class Circle implements Shape {
@Override public void draw() { System.out.println("Inside Circle::draw() method."); }}
复制代码


然后根据传入信息的不同,生成不同的对象


public class ShapeFactory {
//use getShape method to get object of type shape public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square(); }
return null; }}
复制代码


最后 main 方法调用,获取对象


public class FactoryPatternDemo {
public static void main(String[] args) { ShapeFactory shapeFactory = new ShapeFactory();
//get an object of Circle and call its draw method. Shape shape1 = shapeFactory.getShape("CIRCLE");
//call draw method of Circle shape1.draw();
//get an object of Rectangle and call its draw method. Shape shape2 = shapeFactory.getShape("RECTANGLE");
//call draw method of Rectangle shape2.draw();
//get an object of Square and call its draw method. Shape shape3 = shapeFactory.getShape("SQUARE");
//call draw method of circle shape3.draw(); }}
复制代码


这就是工厂模式,工厂实现的方式原理是根据传入的某个参数获取一个对象,一旦我们新增一个 shape 类型,就修改 ShapeFactory 类。这种方式不够灵活,并违背了软件设计的开闭原则。


开闭原则:一个软件实体, 如类, 模块, 函数等应该对扩展开放, 对修改封闭.对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对已有代码进行任何修改。


要想做到不修改的动态查看类的类型,反射就是一个不错的选择。


Java 反射(Reflection)是一个强大的特性,它允许程序在运行时查询、访问和修改类、接口、字段和方法的信息。


那么思路就有了,最后的修改就是这样:


public class ShapeFactory {
private ShapeFactory(){} public static Shape getInstance(String className){ Shape shape = null; try { shape = (Shape) Class.forName(className).newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } return shape; }}
复制代码


而利用反射的代码就封装到了Class.forName里面


@CallerSensitivepublic static Class<?> forName(String className)                throws ClassNotFoundException {     Class<?> caller = Reflection.getCallerClass();     return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}
复制代码


IOC 底层原理主要用到了 3 种技术:工厂模式、反射、XML 解析 XML 文件的配置这里我们不多说,下一章我们具体来说如何做,这里就明白我们的依赖管理都是类似于 maven 中的 pom.xml 一样管理的就行。


工厂模式、反射、XML 解析的结合完成了 IoC 的所有技术概括,形成了巧妙的化学反应,我们后面 spring 都是基于整个概念在前行。


文章转载自:ENchantEd丨

原文链接:https://www.cnblogs.com/ENchantedN/p/18667122

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
IoC究竟是什么?——IoC的基础分析_网络协议_快乐非自愿限量之名_InfoQ写作社区