1 浅谈控制反转和依赖注入-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅谈控制反转和依赖注入

454398 来源:博客园 作者:田园里的蟋蟀 2020-10-29 11:38 次阅读

控制反转(Inversion of Control)是解决程序耦合问题的一种方案,还有种叫法是依赖注入(Dependency Injection),但我感觉Ioc(控制反转)是一种思想,DI(依赖注入)是实现这种思想的一种方式,或者说Ioc是一种概念,DI是这种概念的思想,不知道我这样理解的对不对。可能一开始接触这些东西有点莫名其妙,园友们写的一些东西也看得头疼,至少我当时是这样,如果你是像我一样的菜鸟,请跟我一起学习下,不看代码,我们先看一个生活中的例子-压水井和自来水厂的故事

小时候在农村喝水都是自家打井或是用电水泵取水,想什么时候喝就什么时候喝,想喝多少就喝多少,很方便,而且不用花钱。但是有个问题是,家里面的房子要装修或是重建,原来打的井已经不适合新建的房子了,也就是说需要重新打井,这就很麻烦,建多少次房子,需要打多少次的井(当然土豪才这样)。

我们先看这个小示例,其实如果抽象一点的话,有点类似工厂模式,为什么?我们分析下:上面例子中的水可以看成一个产品,每家的井或是电水泵可以看成一个工厂,自己根据自家的情况来“生产”出来水,只有一家有井或是电水泵还好(其他家去他家取水,但不现实),如果每家都有一个井或是电水泵,就有点工厂泛滥的情况发生了,可能会出现:

水污染:每家都吃不上水,这里面的水出现问题就是产品出现问题,这样我们就需要在每个工厂里面进行处理,就比如需要在每家的井或电水泵上安装一个净水器,显然代价比较大,也不太现实。

整体搬迁:原来的井或电水泵用不了了,每家的井或电水泵就需要重新搞,可能不太现实,当然只是做个假设,细想一下,这个问题的根源其实就是井或电水泵太多了,也就是工厂泛滥。

上面所说的问题为什么会出现?其实就是依赖关系作祟,每一家都要依赖自家的井或电水泵,也没办法,毕竟人要喝水,总不能跑到地下暗河去喝吧,只能通过井或电水泵(工厂)来取水(调用),这个问题在编程中就是依赖倒置原则的反例,何为依赖倒置原则

高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。

抽象不应该依赖于具体,具体应该依赖于抽象。

第一点:高层次模块(使用者)就是每户人家,低层次模块(被使用者)就是压水井或电水泵,可以看出他们都是依赖于具体的对象,而并非依赖于抽象;第二点:水(抽象)依赖压水井或电水泵(具体),人(具体)依赖压水井(具体),而并非具体依赖于抽象。可以看出这两点完全不设和依赖倒置原则,怎么解决问题呢?请看下面。

自来水厂

上面的示例中其实有个三个对象:每户人家、压水井或电水泵、水,就是在探讨他们三个这之间的依赖关系,明确这一点很重要。

随着时代的发展,压水井和电水泵慢慢消失在我们的视野中(当然现在还有很多落后的地方在用,比如像我老家),政府就在每个村庄或是几个村庄之间建设自来水厂,为什么政府要建设自来水厂?难道他们都是搞编程的?知道工厂泛滥的坏处?哈哈,我觉得应该是多收点钱吧,你觉得呢?开个玩笑。

不管政府目的如何,但好像解决了工厂泛滥的一些问题,我们再来分析下有了自来水厂会有什么不同,我们画个示意图看下:

画的比较丑(莫笑),但是简单的意思还是可以表达的,图中的人和水都是抽象的,地下水和水库依赖于于抽象的水,A村的人和B村的人依赖于抽象的人,人和水怎么关系呢?这个就有自来水厂决定了,它让你喝地下水,你就不能喝水库的水。这就基本符合依赖倒置原则:抽象不依赖于具体,具体依赖于抽象

这中间关键在于自来水厂,没了压水井,有了自来水厂,我们看看上面压水井的“工厂泛滥”问题能不能解决?

水污染:比如地下水出现问题,因为自来水厂不依赖地下水,而是依赖于抽象的水,地下水有问题,那我用水库的水,水库的水如果有问题,那我们用雨水净化。。。我们人喝到的不管什么水?反正都是水,不影响我们喝水就行了。

整体搬迁:比如A村的人因为某些原因,要搬到B村,如果是上面压水井的模式,帮过去就需要重新打井了,但是有了自来水厂,我只需要接个管线,按个水龙头就行了,就这么简单。

从上面的分析来看,建设自来水厂确实比压水井可靠多了,回到我们这篇要讲的正题-控制反转(Ioc),你可能也有些明白了,其实自来水厂就可以看做是Ioc,用什么样的水?给什么样的人?都是自来水厂决定,好处就不用多说了,上面已经讲明,套用到编程里面是相同的道理,只可意会哦。

说到这里,你不禁有些惊讶,难道政府里面有系统架构师?哈哈笑过。

上面的示例,下面我们再来用代码复述一下,毕竟理论要结合实践。

压水井的问题-依赖

压水井模式有三个对象:人、压水井、水,我们就用常规的方式简单写下代码:

 1         /// 
 2         /// 村民
 3         /// 
 4         public class VillagePeople
 5         {
 6             public void DrinkWater()
 7             {
 8                 PressWater pw = new PressWater();
 9                 UndergroundWater uw = pw.returnWater();
10                 if (uw!=null)
11                 {
12                     Console.WriteLine("地下水好甜啊!!!");
13                 }
14             }
15         }
16         /// 
17         /// 压水井
18         /// 
19         public class PressWater
20         {
21             public UndergroundWater returnWater()
22             {
23                 return new UndergroundWater();
24             }
25         }
26         /// 
27         /// 地下水
28         /// 
29         public class UndergroundWater
30         { 
31         }

上面的代码就是简单演示村民通过压水井喝水的过程,因为村民不能直接取得水,只能通过压水井取得地下水,很明显我们可以看出之间的依赖关系:

VillagePeople依赖于PressWater

VillagePeople依赖于UndergroundWater

PressWater依赖于UndergroundWater

我们在做业务处理的时候,简单的依赖关系可以用上面的方式处理,如果太复杂的话就不行了,牵一发而动全身总归不是很好。

大家可能说,上面不是讲到“工厂泛滥”问题,这边怎么没指出?因为PressWater某一方面其实就可以看做一个小工厂,每家的压水井不一样,这边只是说某一种,“工厂泛滥”其实就是依赖作祟,上面的例子说明的是依赖关系,一样的道理,所以下面就用这个例子来做一些东西。

压水井的问题解决-依赖倒置

我们在讲压水井的时候提到过依赖倒置原则,这边就不再说了,因为VillagePeople依赖于PressWater、VillagePeople依赖于UndergroundWater、PressWater依赖于UndergroundWater,我们可以把PressWater(压水井)和UndergroundWater(地下水)抽象出来,UndergroundWater属于水的一种,可以抽象为IWater,PressWater因为是获取水的方式之一,可以抽象为IWaterTool,这边就要面向接口编程了,根据依赖倒置原则,我们把上面的代码修改一下:

 1         /// 
 2         /// 村民
 3         /// 
 4         public class VillagePeople
 5         {
 6             public void DrinkWater()
 7             {
 8                 IWaterTool pw = new PressWater();
 9                 IWater uw = pw.returnWater();
10                 if (uw != null)
11                 {
12                     Console.WriteLine("水好甜啊!!!");
13                 }
14             }
15         }
16         /// 
17         /// 压水井
18         /// 
19         public class PressWater : IWaterTool
20         {
21             public IWater returnWater()
22             {
23                 return new UndergroundWater();
24             }
25         }
26         /// 
27         /// 获取水方式接口
28         /// 
29         public interface IWaterTool
30         {
31             IWater returnWater();
32         }
33         /// 
34         /// 地下水
35         /// 
36         public class UndergroundWater : IWater
37         { }
38         /// 
39         /// 水接口
40         /// 
41         public interface IWater
42         { }

从上面的代码可以看出,UndergroundWater依赖接口IWater,PressWater依赖IWaterTool和IWater,VillagePeople依赖IWaterTool和IWater,这样就符合依赖倒置原则了,都是依赖于抽象,从而降低耦合度,这样当一个方式变化了不会影响到其他,地下水污染了,我可以通过别的获取工具获取水,而不至于没水喝。

但是上面说的忽略了个问题,接口总是会被实现的,也就是总会执行:IWaterTool pw =newPressWater();这样耦合度就产生了,也就是VillagePeople依赖于PressWater,我们可以通过工厂参数来产生不同的获取工具对象,这种方式表面上虽然解决了问题,但是实质上代码耦合度并没有改变,怎么办呢?请接着往下看。

自来水厂-Ioc

通过Ioc模式可以彻底解决上面我们提到耦合的问题,它把耦合从代码中移出去,放到统一的XML文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中。就像自来水厂一样,水的来源、水的去处都是它来决定,人们只要通过它来喝水就行了,而不需要考虑的太多。

早在微软提供的一个示例框架PetShop中就有Ioc的体现,只不过那时候不太懂,PetShop是通过反射创建对象,上面的代码我们修改一下:

 1         /// 
 2         /// 村民
 3         /// 
 4         public class VillagePeople
 5         {
 6             public void DrinkWater()
 7             {
 8                 IWaterTool pw = (IWaterTool)Assembly.Load(ConfigurationManager.AppSettings["AssemName"]).CreateInstance(ConfigurationManager.AppSettings["WaterToolName"]);
 9                 IWater uw = pw.returnWater();
10                 if (uw != null)
11                 {
12                     Console.WriteLine("水好甜啊!!!");
13                 }
14             }
15         }

上面代码中我们只需要在配置文件中添加获取水工具的名称WaterToolName就行了,因为一种工具对应获取特定的一种水,所以水的种类不需要配置。地下水污染了,我们只需要在配置文件中修改一下WaterToolName就可以了。

Ioc模式,系统中通过引入实现了Ioc模式的Ioc容器,即可由Ioc容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。

看到这里,是不是感觉Ioc模式有点“热插拔”的意思?有点像USB一样呢?

自来水厂运行-DI

如果把自来水厂看做Ioc,那我觉得依赖注入(DI)就是这个自来水厂的运行模式,当然其实是一个意思,依赖注入是什么?全称Dependency Injection,我们从字面上理解下:需要的接口实现注入到需要它的类中,这就是依赖注入的意思。自来水厂获取水源的时候,控制这个获取水源的开关可以看做是依赖注入的一种体现,话不多说,懂得就好。

依赖注入的方式有很多,就像控制获取水源的开关有很多一样。

构造器注入(Constructor Injection):Ioc容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,Ioc容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;

属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,Ioc容器会自动初始化该属性;

方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,Ioc容器会自动调用该方法。

有时间可以好好研究下依赖注入的各种方式,这边我们就使用微软提供的Unity实现依赖注入,方式是构造器注入,首先使用Nuget工具将Unity添加到项目中,安装Unity需要.net framework4.5支持。

添加完之后,发下项目中多了Microsoft.Practices.Unity和Microsoft.Practices.Configuation两个dll,代码如下:

 1         /// 
 2         /// 人接口
 3         /// 
 4         public interface IPeople
 5         {
 6             void DrinkWater();
 7         }
 8         /// 
 9         /// 村民
10         /// 
11         public class VillagePeople : IPeople
12         {
13             IWaterTool _pw;
14             public VillagePeople(IWaterTool pw)
15             {
16                 _pw = pw;
17             }
18             public void DrinkWater()
19             {
20                 IWater uw = _pw.returnWater();
21                 if (uw != null)
22                 {
23                     Console.WriteLine("水好甜啊!!!");
24                 }
25             }
26         }

调用代码:

1         static void Main(string[] args)
2         {
3             UnityContainer container = new UnityContainer();
4             container.RegisterType();
5             TestFour.IPeople people = container.Resolve();
6             people.DrinkWater();
7         }

首先我们创建一个Unity容器,接下来我们需要在容器中注册一种类型,它是一个类型的映射,接口类型是IWaterTool,返回类型为PressWater,这个过程中就是要告诉容易我要注册的类型。

比如自来水厂要用地下水作为水源,这时候操作员输入命令,就是RegisterType,参数为IWaterTool、PressWater,下面就是调用Resolve生成对象,这个过程表示要把水输送到哪户人家,命令是Resolve,参数为VillagePeople,接下来就是直接打开水龙头喝水了,很方便吧。

关于依赖注入其实有很多的东西,上面的示例只是抛砖引玉,有时间的话好好研究下,比如依赖注入的其他方式等等。
编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • IOC
    IOC
    +关注

    关注

    0

    文章

    28

    浏览量

    10099
  • Unity
    +关注

    关注

    1

    文章

    127

    浏览量

    21804
收藏 人收藏

    评论

    相关推荐

    PLC控制电机正反转的物联网解决方案

    机床工作台的前进与后退、机床主轴的正转与反转、升降机的上升与下降等。为了满足这些要求,PLC控制电动机必须能够实现正反转。 为实现对这类自动化设备的远程监控,数之能提供高效可靠的物联网解决方案。通过将数之能数据中台接入控
    的头像 发表于 11-19 15:53 190次阅读
    PLC<b class='flag-5'>控制</b>电机正<b class='flag-5'>反转</b>的物联网解决方案

    什么是电机正反转?电机正反转主要应用在哪些方面?

    能力。 电机正反转的基本原理 电机正反转的实现主要依赖于电机的控制方式。在交流电机中,通过改变电源的相序或使用变频器改变电源的频率和相位,可以实现电机的正
    的头像 发表于 10-24 13:56 1451次阅读

    drv8412是怎么控制电机正反转的?

    请问drv8412是怎么控制电机的正反转,谢谢
    发表于 09-20 07:13

    无刷电机正反转由什么控制

    无刷电机(Brushless DC Motor, BLDC)是一种没有电刷的电机,它通过电子换向器来控制电流的流向,从而实现电机的转动。无刷电机的正反转控制是其应用中的一个重要方面,涉及到电机的驱动
    的头像 发表于 09-03 14:14 684次阅读

    如何将行程开关接入正反转控制电路

    行程开关是一种常见的限位开关,用于控制机械设备的行程。在正反转控制电路中,行程开关可以用于实现自动控制,当设备到达预定位置时,自动切换到反向运动。以下是将行程开关接入正
    的头像 发表于 08-26 16:35 650次阅读

    ESP32控制舵机的正反转原理是什么

    舵机是一种将电信号转换为机械运动的设备,广泛应用于机器人、无人机、汽车等领域。ESP32是一款功能强大的微控制器,具有丰富的外设接口和高性能的处理能力,可以方便地控制舵机实现正反转等动作。本文将介绍
    的头像 发表于 08-20 09:13 1047次阅读

    变频器控制电机的正反转控制方式有哪些?

    变频器是一种广泛应用于工业领域的电力调整设备,它可以通过改变电源的频率来实现电机的速度控制。在许多生产过程中,电机的正反转控制是非常重要的,变频器能够通过不同的控制方式来实现这一功能。
    的头像 发表于 08-14 17:04 1639次阅读

    简单介绍plc如何控制电机正反转

    PLC(可编程逻辑控制器)控制电机正反转的过程可以通过以下步骤清晰地表示和归纳: 一、了解电机正反转工作原理 电机正反转是指电机能够实现顺时
    的头像 发表于 07-29 10:37 942次阅读

    两相正反转开关怎么接线方法

    两相正反转开关是一种常见的电气设备,主要用于实现电动机的正反转控制。在实际应用中,正确接线是保证设备正常运行的关键。本文将介绍两相正反转开关的接线方法,包括原理、步骤和注意事项。 一、
    的头像 发表于 07-19 10:48 3343次阅读

    v20变频器控制电机反转怎么调

    以下是一些关于V20变频器控制电机反转的基本步骤和注意事项。 确认电机和变频器的型号和规格是否匹配。 确保电机和变频器的接线正确,包括电源线、控制线和电机线。 检查变频器的设置,确保其工作在正确
    的头像 发表于 06-18 09:46 2331次阅读

    PLC控制电动机正反转电路的工作原理

    在工业自动化控制中,电动机作为动力源,其正反转控制是常见的控制需求。传统的电动机正反转控制主要
    的头像 发表于 06-17 09:37 1984次阅读

    步进电机的正反转实现方法

    步进电机,作为一种将电脉冲信号转换为角位移或线位移的精密控制电机,其正反转的实现对于许多应用至关重要。本文将深入探讨步进电机正反转的实现方法,从基本的控制原理到具体的实现步骤,再到实际
    的头像 发表于 06-14 09:58 2646次阅读

    直流电机正反转控制方式

    直流电机,作为一种将直流电能转化为机械能的电动机,广泛应用于各种工业和民用领域。在实际应用中,经常需要实现直流电机的正反转,以满足不同的工作需求。本文将详细介绍直流电机正反转的实现方法,包括基本原理、控制方式和具体应用案例,以期
    的头像 发表于 06-04 17:20 4691次阅读

    什么是离子注入?离子注入的应用介绍

    离子注入是将高能离子注入半导体衬底的晶格中来改变衬底材料的电学性能的掺杂工艺。通过注入能量、角度和剂量即可控制掺杂浓度和深度,相较于传统的扩散工艺更为精确。
    的头像 发表于 02-21 10:23 5055次阅读
    什么是离子<b class='flag-5'>注入</b>?离子<b class='flag-5'>注入</b>的应用介绍

    plc怎么控制伺服电机正反转

    电机的正反转,本文将详细介绍其中的几种方法。 一、硬件控制 开关控制:最简单的方法是使用开关控制伺服电机的正反转。通过将开关接入PLC的输入
    的头像 发表于 12-25 11:16 4267次阅读