首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

架构演化学习思考(4) --- IOC的学习认识

编程知识
2024年08月05日 20:37

架构演化学习思考(4)

IOC的学习认识

IOC相关概念认识

什么是IOC?

IOC全称为 Inversion Of Control ,即控制反转。它是一种控制思想,可以解释为类和类之间的依赖关系不再由代码直接控制,而是通过容器来控制和配置实现。

控制反转?那么什么是正传? 反转有啥好处?IOC到底是啥?

好,那就开始逐步认识和了解吧~

既然是一种思想,那就从它的常见实现方式DI来入手。

DI

DI,即 Dependence Injection,依赖注入,它是IOC的一种实现。

依赖这个概念我们在第一篇文章中解释过,它是一种对象/引用的持有关系。

最简单的单项依赖:

A对B产生依赖关系。

public class A
{
    public B b = new B();
}

那么注入又是什么?

注入是建立依赖关系的过程。

public class A
{
    //持有B的空引用
    public B b =null;
}


public class B {}

void Main()
{
    var a = new A();
    var b = new B();

    //建立AB之间的依赖关系
    //即注入
    a.b = b;
}

也就是说,A和B建立依赖的过程便是注入。因为注入操作。使的A中b引用不再为空,而是直接拿到B对象的引用。

到此我们认识了”依赖注入“的操作,就是帮助建立对象与对象之间的依赖关系,让A对象完成持有B对象引用的操作。

IOCContainer的使用

DI的具体实现离不开DI容器,DI容器有时候也称为IOC容器,通过此容器来完成依赖注入的工作。来看一下IOCContainer的使用。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using QFramework

namespace IOCContainerExample
{

    public class A
    {
        public void Say()
        {
            Debug.Log("我是A" + this.GetHashCode());
        }
    }

    public class IOCExample: MonoBehaviour
    {
        //添加注入标记
        [Inject]
        public A a {get;set;}

        void Start()
        {
            //创建容器实例
            var container = new QFrameworkContainer();

            //注册需要注入的类型
            container.Register<A>();

            //进行依赖注入
            //会自动查找 Inject Atrribute的对象
            container.Inject(this);

            //注入之后可以使用了
            a.Say();
        }
    }
    
}

使用IOCContainer来进行依赖注入,先给容器注册相关的类型,然后标记要获取对象的空引用,之后调用容器的Inject方法完成依赖注入。

这个这个容器可以理解为一个”租房中介“,想要出租房屋的房东们向中介进行”Register“注册,当调用Inject()方法时候,中介会根据租客( [Inject]标记的空引用)的租房类型来匹配合适的房源,完成租客找房源的目的,帮助租客完成对房子的依赖。

这就是IOCContainer的简单使用。

image

一般情况下,DIContainer会提供如下的API:

  • 注册类型:Register<TSource,TTarget>
  • 注入:Inject(object obj)
  • 解析:Resolve()

注册和注入我们在上面内容中使用了解了,而解析(Resolve)是什么意思呢?

Resolve实际上会根据类型返回实例。

那会产生疑问,返回的实例是每次都新建的还是同一个实例呢?

在下面内容会逐步解析。

我们继续来聊IOCContainer的职责,正如上文所比喻的那样,其职责就是管理依赖和注入依赖。通过类类型来管理依赖,给空引用赋值完成依赖注入。也就是说租房中介帮助想要找房源的租客和登记注册的房源之间建立关系,具体表现就是建立依赖。

这个中介手里掌握着租客和房源之间的对应关系,也可以对此依赖关系履行管理职责。所以一般的IOCContainer会用一个Dictionary<Type,object>来作为核心数据结构。

即根据Type可以得到Type的实例,依赖在代码中就是这样一个东西。而这个依赖不是指Type和object之间的相互依赖,而是说其结构本身就是一个依赖。

这里比较绕,也就是说这里的数据结构中存储的键值对元素本身就是依赖,是Inject(object obj) 中的obj的依赖。

即,object 类中有期待注入的null类型的索引对这种配对信息产生依赖。比喻过去就是“租客名单”(object)对中介手中的(租客房源配对信息)有依赖,因为租客在配对信息中等级(object)才可以顺利的和自已想要的房源进行配对。

不知道大家这里还能顺利理解嘛!这就是IOCContainer中对依赖的管理方式。

没关系,我们下面会看具体的实例。

我们在看案例之前先简单聊一聊IOCContainer的强大之处。

IOCContainer的强大之处

用过单例的人都会有这样的认识:用单例一时爽,一直用单例一直爽!

因为单例作为随用随获取,与其它模块的交互变得非常容易。

项目规模小的时候这样确实很方便,不用单独处理各种依赖关系。只要大家按照约定的层级关系来访问代理模块既可。

但是如果项目功能繁多,模块层级也有多个,约定的内容也难以继续保证,且单从技术限制角度来说,单例访问是没有限制的,像是对所有的层级模块都开放使用。这样确实难以体现出模块之间的层级关系,显然不利于整个项目的架构设计。

那么如何合理使用单例,避免破坏层级呢?

解决方式很简单,就是最顶层的模块都用单例,然后一些底层模块,作为顶层模块的成员变量,从而达到逻辑层无法直接访问底层模块,而是必须通过顶层模块间接地使用底层模块的服务

img

这样就解决了单例结构无法表达层级问题,但是同时也失去了单例带来的种种好处

,但是同样失去了单例的种种好处,易扩展。

因为现在单例结果对底层的模块产生了依赖关系,当拓展功能模块时候要考虑对底层模块的依赖关系。

那么IOCContainer的强大之处体现出来,帮助管理依赖关系。

依赖管理

我们回过头来再看依赖管理相关内容,依赖注入的时机和位置是一个需要关注的问题。

public class ModuleA
{
    public ModuleB moduleB;
}

public class ModuleB
{
    
}

来看一个待注入的依赖,依赖注入我们可以再ModuleA内部进行:

public class ModuleA
{
    public ModuleB moduleB = new ModuleB();
}

但是如果MoudleB是公用的呢?在内部创建显然就不太合适了,因为这里是一个模块,不是简单的一个对象。

那就在外部创建对象:

void main()
{
    var moduleA = new ModuleA;
    mouduleA.moudleB = new MoudleB();
}

那在外部注入的依赖在模块内部使用时候就得需要知道依赖到底注入没有?

在哪里注入的?我可不可以直接用?

public class ModuleA
{
    public ModuleB moduleB;
    /*
     ..。其它代码逻辑
    */
    void someFunc()
    {
        //需要使用moduleB
        //需要知道moduleB到底有没有值?在哪里获取到的?
        moduleB.XXX  
    }
}

所以这就不得不考虑依赖的创建过程了。

而使用单例,那就没有这个问题。

public class ModuleA
{
   void someFunc()
   {
       //直接使用单例
       moduleB.Instance.DoSomething();
   }
}

现在该IOCContainer登场了。

是的,IOCContainer的职责就是注入依赖、管理依赖。

使用IOCContainer管理依赖

public class ModuleA
{
    [Inject]
    public ModuleB moduleB;

    void something()
    {
        //放心使用 不用考虑是否为空
        moduleB.DoSomeThing();
    }
}

在启动程序的时候,统一注册依赖:

public static QFrameworkContainer Container {get; set;}

void Main()
{
    Container = new Container();

    Container.Register<MoudleB>();
}

在MoudleA的构造函数中注入依赖:

public class ModuleA
{
    [Inject]
    public ModuleB moduleB;

    //构造函数
    public MoudleA()
    {
        //注入依赖
        Global.ContainerInject(this);
    }

    void something()
    {
        //放心使用 不用考虑是否为空
        moduleB.DoSomeThing();
    }
}

这样使用IOCContainer对各种依赖进行管理其模块内容变得更佳清晰:

img

放心使用依赖内容,依赖管理和注入交给IOCContainer管理即可。

From:https://www.cnblogs.com/TonyCode/p/18344109
本文地址: http://www.shuzixingkong.net/article/819
0评论
提交 加载更多评论
其他文章 在oracle中将一行字符串拆分成多行
例如,有如下一张表,表名为bk_test。插入了以下数据: CREATE TABLE BK_TESK(id varchar2(10),s varchar2(20)); insert into BK_TESK values (&#39;A&#39;,&#39;1,2,3&#39;); insert i
在oracle中将一行字符串拆分成多行 在oracle中将一行字符串拆分成多行
代码随想录Day5
242.有效的字母异位词 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 示例 1: 输入: s = &quot;anagram&quot;, t = &quot;nagaram&
一些八股:1.fetch 的理解。2.let、const、var
一、 说说你对 Fetch 的理解,它有哪些优点和不足? Fetch API 是现代 JavaScript 中用于进行网络请求的接口,旨在替代传统的 XMLHttpRequest。它提供了一种更简单、更灵活的方法来获取资源和与服务器进行交互。下面,我将详细介绍 Fetch 的优点和不足。 Fetch
南京大学计算机基础(四)踩坑笔记
第六周 缓冲区溢出章节 bang问题: 每次输入的id将影响getbuf中的堆栈位置,用-u 12的ebp和-u 123的ebp位置就不一样。 注意汇编代码中不能出现a0(代表换行符),如果地址有a0就将它随便改改就行了(a0改为a8-0x8)。 注意gdb如果不能重定向,可能是因为你修改了gdbi
南京大学计算机基础(四)踩坑笔记 南京大学计算机基础(四)踩坑笔记 南京大学计算机基础(四)踩坑笔记
【Playwright+Python】系列教程(七)使用Playwright进行API接口测试
playwright也是可以做接口测试的,但个人觉得还是没有requests库强大,但和selenium相比的话,略胜一筹,毕竟支持API登录,也就是说可以不用交互直接调用接口操作了。 怎么用 既然是API的测试了,那肯定就别搞UI自动化那套,搞什么浏览器交互,那叫啥API测试,纯属扯淡。 也不像有
【Playwright+Python】系列教程(七)使用Playwright进行API接口测试 【Playwright+Python】系列教程(七)使用Playwright进行API接口测试 【Playwright+Python】系列教程(七)使用Playwright进行API接口测试
算法·理论:Manacher 笔记
\(\text{Manacher}\) 来啦! \(\text{Manacher}\) 并没有什么前置知识,比 \(\text{KMP}\) 简单多了。 前置处理 \(\text{Manacher}\) 算法用于解决回文串相关问题,先看几个基本概念:回文中心、回文半径,这些看字面意思就能猜到。 还有
数据结构 顺序与链式二叉树的原理与实现(万字)
目录一、树概念及结构树的概念树的相关概念树的表示二、二叉树概念及结构概念特殊的二叉树二叉树的性质二叉树的存储结构三、二叉树的顺序结构及实现二叉树的顺序结构堆的概念及结构堆的实现堆向下调整算法堆的创建建堆时间复杂度堆的插入堆的删除堆的代码实现Heap.hHeap.c堆的应用堆排序堆排序代码实现Heap
数据结构 顺序与链式二叉树的原理与实现(万字) 数据结构 顺序与链式二叉树的原理与实现(万字) 数据结构 顺序与链式二叉树的原理与实现(万字)
在Vue3中如何为路由Query参数标注类型
与以往的OOP或者Class方案不同,Zova在界面交互层面仍然采用Setup语法,仅仅在业务层面引入IOC容器。IOC容器犹如一把钥匙,为我们打开了业务工程化的大门,允许我们探索更多工程化方面的设计和能力。
在Vue3中如何为路由Query参数标注类型 在Vue3中如何为路由Query参数标注类型 在Vue3中如何为路由Query参数标注类型