谈谈对IOC及DI的理解与思考

一、前言

在实际的开发过程中,我们经常会遇到这样的情况,在进行调试分析问题的时候,经常需要记录日志信息,这时可以采用输出到控制台。

因此,我们通常会定义一个日志类,来实现输出日志。

定义一个生成验证的逻辑处理方法,

    public class Logger{public void AddLogger(){Console.WriteLine("日志新增成功!");}}

然后在控制台中输出结果。

        static void Main(string[] args){Logger logger = new Logger();logger.AddLogger();Console.Read();}

看着实现的结果,我们以为完成任务了,当其实这才是刚刚开始。

二、开始

相信大家在开发中,都会遇到这种情况,有时需要控制台输出,但也有可能要你输出到文本,数据库或者远程服务器等等,这些都是有可能。因此最初采用直接输出到控制台已经不能满足条件了,所以我们需要将上述代码进行重写改造,实现不同的输出方式。

2.1 第一种方式

2.1.1 控制台方式

用到控制台方式输出的时候:

    /// <summary>/// 控制台输出/// </summary>public class ConsoleLogger{public void AddLogger(){Console.WriteLine("控制台输出:日志新增成功!");}}

定义一个获取输出日志的处理逻辑类,因此我们需要定义ConsoleLogger类并初始化

    /// <summary>/// 定义一个输出日志的统一类/// </summary>public class LoggerServer{private readonly ConsoleLogger consoleLogger = new ConsoleLogger();//添加一个私有变量的对象 个私有变量的数字对象 public void AddLogger(){consoleLogger.AddLogger();}}

控制台输出结果:

    static void Main(string[] args){LoggerServer loggerServer = new LoggerServer();loggerServer.AddLogger();Console.Read();}

「控制台输出:日志新增成功!」

2.1.2 文本输出

当用到文本输出日志的时候,我们再次定义一个生成文本日志的方式

    /// <summary>/// 文本输出/// </summary>public class FileLogger{public void AddLogger(){Console.WriteLine("文本输出:日志新增成功!");}}

然后再次定义一个获取验证的处理逻辑类,因此我们需要定义ImageVerification类并初始化

    /// <summary>/// 定义一个输出日志的统一类/// </summary>public class LoggerServer{private readonly FileLogger fileLogger = new FileLogger();//添加一个私有变量的对象 public void AddLogger(){fileLogger.AddLogger();}}

最后输出结果:

「文本输出:日志新增成功!」

通过以上的方式,我们实现了不同方式输出不同日志方式,但是仔细观察可以发现,这种方式不是一种良好的软件设计方式。

所以可能有人会改成下面第二种方式,以接口来实现。

2.2 第二种方式

定义一个ILogger接口并声明一个AddLogger方法

    public interface ILogger{void AddLogger();}

在控制台输出方式中ConsoleLogger类,实现ILogger接口

    /// <summary>/// 控制台输出/// </summary>public class ConsoleLogger : ILogger{public void AddLogger(){Console.WriteLine("控制台输出:日志新增成功!");}}

在文本输出日志方式FileLogger类中,实现ILogger接口

    /// <summary>/// 文本输出/// </summary>public class FileLogger : ILogger{public void AddLogger(){Console.WriteLine("文本输出:日志新增成功!");}}

定义一个统一的输出日志类LoggerServer

/// <summary>
/// 定义一个获取验证的统一类
/// </summary>
public class VerificationServer
{private readonly ConsoleLogger consoleLogger = new ConsoleLogger();//添加一个私有变量的对象 //private readonly FileLogger fileLogger = new FileLogger();//添加一个私有变量的对象 public void AddLogger(){_logger.AddLogger();}
}

最后,控制台调用输出:

    static void Main(string[] args){LoggerServer loggerServer = new LoggerServer();loggerServer.AddLogger();Console.Read();}

「控制台输出:日志新增成功!」

虽然第二种方式中,采用了接口来实现,降低耦合,但还是没有达到我们想要的效果,因此以上的两种方式,都不是很好的软件设计方式。

代码可拓展性比较差,以及组件之间存在高度耦合,违反了开放关闭原则,在设计的时候,也应当考虑「对扩展开放,对修改关闭」

2.3 思考

既然要遵循开放关闭原则,那上面的写法,选择采用控制台日志输出方式所需要的ConsoleLogger创建和依赖都是在统一的日志类LoggerServer内部进行的,既然要使内部不直接存在绑定依赖,那有没有什么方式从外部传递的方式给LoggerServer类内部引用使用呢?

三、引入

3.1 依赖注入

「依赖注入」 : 它提供一种机制,将需要依赖对象的引用传递给被依赖对象。

下面我们先看看具体的几种注入方式,再做小结说明。

3.1.1 构造函数注入

LoggerServer类中,定义一个私有变量_logger, 然后通过构造函数的方式传递依赖

public class LoggerServer
{private ILogger _logger; //1. 定义私有变量//2.构造函数public LoggerServer(ILogger logger){//3.注入 ,传递依赖this._logger = logger; }public void AddLogger(){_logger.AddLogger();}
}

通过控制台程序调用,先在外部创建依赖对象,而后通过构造的方式注入依赖

        static void Main(string[] args){#region 构造函数注入// 注入控制台输出方式// 外部创建依赖的对象 -> ConsoleLoggerConsoleLogger console = new ConsoleLogger();// 通过构造函数注入 -> LoggerServerLoggerServer loggerServer1 = new LoggerServer(console);loggerServer1.AddLogger();// 注入 文件输出方式FileLogger file = new FileLogger();// 通过构造函数注入 -> LoggerServerLoggerServer loggerServer2 = new LoggerServer(file);loggerServer2.AddLogger();#endregionConsole.Read();}

输出:

控制台输出:日志新增成功!

文本输出:日志新增成功!

显然的发现,通过这种构造函数注入的方式,在外部定义依赖,降低内部的耦合度,同时也增加了扩展性,只需从外部修改依赖,就可以实现不同的验证结果。

3.1.2 属性注入

即通过定义一个属性来传递依赖

    /// <summary>/// 定义一个输出日志的统一类/// </summary>public class LoggerServer{//1.定义一个属性,可接收外部赋值依赖public ILogger _logger { get; set; }public void AddLogger(){_logger.AddLogger();}}

通过控制台,定义不同的方式,通过不同依赖赋值,实现不同的验证结果:

        static void Main(string[] args){#region 属性注入// 注入 控制台输出方式//外部创建依赖的对象 -> ConsoleLoggerConsoleLogger console = new ConsoleLogger();LoggerServer loggerServer1 = new LoggerServer();//给内部的属性赋值loggerServer1._logger = console;loggerServer1.AddLogger();// 注入 文件输出方式//外部创建依赖的对象 -> FileLoggerFileLogger file = new FileLogger();LoggerServer loggerServer2 = new LoggerServer();//给内部的属性赋值loggerServer2._logger = file;loggerServer2.AddLogger();#endregionConsole.Read();}

输出

控制台输出:日志新增成功!

文本输出:日志新增成功!

3.1.3 接口注入

先定义一个接口,包含一个设置依赖的方法。

    public interface IDependent{void SetDepend(ILogger logger);//设置依赖项}

这个与之前的注入方式不一样,而是通过在类中「继承并实现这个接口」

    public class VerificationServer : IDependent{private ILogger _logger;// 继承接口,并实现依赖项方法,注入依赖public void SetDepend(ILogger logger){_logger = logger;}public void AddLogger(){_logger.AddLogger();}}

通过调用,直接通过依赖项方法,传递依赖

        static void Main(string[] args){#region 接口注入// 注入 控制台输出方式//外部创建依赖的对象 -> ConsoleLoggerConsoleLogger console = new ConsoleLogger();LoggerServer loggerServer1 = new LoggerServer();//给内部赋值,通过接口的方式传递loggerServer1.SetDepend(console);loggerServer1.AddLogger();//注入  文件输出方式//外部创建依赖的对象 -> FileLoggerFileLogger file = new FileLogger();LoggerServer loggerServer2 = new LoggerServer();//给内部赋值,通过接口的方式传递loggerServer2.SetDepend(file);loggerServer2.AddLogger();#endregionConsole.Read();}

输出

控制台输出:日志新增成功!

文本输出:日志新增成功!

3.1.4 小结

「依赖注入(DI—Dependency Injection)」

「它提供一种机制,将需要依赖对象的引用传递给被依赖对象」通过DI,我们可以在LoggerServer类在外部ConsoleLogger对象的引用传递给LoggerServer类对象。 注入某个对象所需要的外部资源(包括对象、资源、常量数据)

依赖注入把对象的创造交给外部去管理,很好的解决了代码紧耦合的问题,是一种让代码实现松耦合的机制。 松耦合让代码更具灵活性,能更好地应对需求变动,以及方便单元测试。

3.2 IOC

「控制反转」(Inversion of Control,缩写为「IoC」),在面向对象编程中,是一种「软件设计模式」,教我们如何设计出更优良,更具有松耦合的程序。

在上文的例子中,我们发现如果在获取对象的过程中靠类内部主动创建依赖对象,则会导致代码直接高度耦合并且期存在难以维护这种隐患,所以为了避免这种问题,我们采用了由外部提供依赖对象,内部对象类被创建的时候,将其所依赖的对象引用传递给它,实现了依赖被注入到对象中去。

通俗的说明:

在类A中用到了类B的对象时候,一般情况下,需要在A的代码中显式的new一个B的对象。这种方式都是通过我们自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个对象,就主动去创建,创建对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起。

public class A
{
private B b = new B();//主动的new一个B的对象。主动创建出来
public void Get()
{B.Create();
}
}

采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。现在创建对象而是有第三方控制创建,你要什么对象,它就给你什么对象,依赖关系就变了,原先的依赖关系就没了,A和B之间耦合度也就减少了。

public class A
{
private B b;//外部new出来, 注入到引用中
public void Get()
{B.Create();
}
}

3.3 关系

「控制反转(IoC)」 是一种软件设计的模式,指导我们设计出更优良,更具有松耦合的程序,

而具体的实现方式有「依赖注入」「依赖查找」

在这一篇主要说的是常用的「依赖注入」方式。

你在实际开发中,可能还会听到另一名词叫 「IoC容器」,这其实是一个依赖注入的「框架」

用来映射依赖,管理对象创建和生存周期。 (在后续篇章会具体说明)

四、思考

说到依赖,就想到依赖注入和工厂模式这两者的区别?

这是网上有一个对比例子:


工厂设计模式依赖注入
对象创建它用于创建对象。我们有单独的Factory类,其中包含创建逻辑。它负责创建和注入对象。
对象的状态它负责创建有状态对象。负责创建无状态对象
运行时/编译时间在编译时创建对象在运行时配置对象
代码变更如果业务需求发生变化,则可能会更改对象创建逻辑。无需更改代码
机制类依赖于工厂方法,而工厂方法又依赖于具体类父对象和所有从属对象可以在单个位置创建

好啦,这篇文章就先讲述到这里吧,「在后续篇章中会对常用的IOC容器进行使用说明」,希望对大家有所帮助。

如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。????

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

终于有人把云计算、大数据和人工智能讲明白了!

今天跟大家讲讲云计算、大数据和人工智能。为什么讲这三个东西呢&#xff1f;因为这三个东西现在非常火&#xff0c;并且它们之间好像互相有关系&#xff1a;一般谈云计算的时候会提到大数据、谈人工智能的时候会提大数据、谈人工智能的时候会提云计算……感觉三者之间相辅相成…

聊一聊如何在.NET Core中使用Nacos 2.0

前言 随着 nacos 2.0.0 正式发布&#xff0c;老黄这边也跟着发布了 1.0.2 版本的 nacos-sdk-csharp。下面简单聊一下相关的使用。纯 SDK 的使用 首先要安装 nuget 包<PackageReference Include"nacos-sdk-csharp" Version"1.0.2" />这里有一点要注意…

听说你刚转了Java?准备转回.NET吧!

牛年跳槽季如火如荼&#xff0c;敏锐点的小伙伴儿应该都留意到了&#xff0c;BAT新年都在招聘.NET。2020年11月份发布.NET5统一了七大应用方向&#xff0c;.NET6的2个预览版已经把Blazor升级、跨平台UI方案MAUI、CLR性能优化等都放出来了&#xff0c;.NET前景充满想象&#xff…

一个人开始废掉的3种迹象

1作家李尚龙说&#xff1a;“在大城市里&#xff0c;搞废一个人的方式特别简单。给你一个安静狭小的空间&#xff0c;给你一根网线&#xff0c;最好再加一个外卖电话。好了&#xff0c;你开始废了。”之前的我并不相信人会这么轻易地堕落&#xff0c;直到身边出现了一个真实的例…

WPF 动态更换图片路径

wpf中动态修改图片路径&#xff0c;其实很简单&#xff0c;有个小伙伴有疑问&#xff0c;绑定了source,为什么不能显示图片呢。。。通过绑定&#xff0c;修改图片路径&#xff0c;动态显示图片&#xff0c;效果如下&#xff1a;图片支持本地路径和网络路径&#xff0c;下面就来…

听说你在学习算法?清华教授为你准备了一份独家秘籍!

也许您渴望进入国内外IT名企&#xff0c;却屡屡在算法类面试中折戟&#xff0c;苦恼不知如何破解困境&#xff1b;也许您是一位IT工程师&#xff0c;却因为算法能力不足而错过无数次的升职加薪&#xff0c;困扰不知如何突破瓶颈&#xff1b;也许您是一位大学生/中学生&#xff…

用VirtualWifi软件实现无线网卡同时连接多个AP。

2019独角兽企业重金招聘Python工程师标准>>> 用VirtualWifi软件实现无线网卡同时连接多个AP。 随着信息技术的发展&#xff0c;越来越多的人开始使用便捷的无线路由器&#xff08;AP&#xff09;组建家庭局域网。当你发现你周围有多个AP可以连接的时候&#xff0c;是…

机器学习萌新必学的Top10算法

导读&#xff1a;在机器学习领域里&#xff0c;不存在一种万能的算法可以完美解决所有问题&#xff0c;尤其是像预测建模的监督学习里。所以&#xff0c;针对你要解决的问题&#xff0c;最好是尝试多种不同的算法。并借一个测试集来评估不同算法之间的表现&#xff0c;最后选出…

分支结构程序案例c语言,C语言学习之三——分支结构程序

运算符优先级&#xff1a;如下所示&#xff1a;image.png关系运算符和表达式关系运算符定义&#xff1a;在程序中经常需要比较两个量的大小关系,以决定程序下一步的工作。因此使用关系运算符进行比较(注&#xff1a;比较后的返回值为boolean值&#xff0c;即0或1(真为1&#xf…

诺基亚首款Windows Phone智能手机将在年内推出

诺基亚高级官员星期日称&#xff0c;第一款采用微软Windows Phone操作系统的诺基亚智能手机的目标发布日期将在几天之内确定下来。诺基亚的目标是在将这种手机推向市场。诺基亚负责智能设备的执行副总裁乔哈洛&#xff08;Jo Harlow&#xff09;称&#xff0c;诺基亚和微软的技…

IOT必备之MQTT结构分析,不进来看看?【后附源码】

全网唯一物联网MQTT协议报文结构分析以及基于C#代码的报文组装实现介绍MQTT是一种基于TCP/IP协议的应用层协议&#xff0c;它规定了不同应用之间进行数据交换时的传送格式。既然是协议&#xff0c;理论上可以被任何开发语言实现它&#xff0c;以运行在任何平台&#xff0c;这个…

要成为年薪五十万的数据分析师,除了技术还需要什么?

超过59%企业&#xff0c;将提高数据分析岗位数量----一流的数据分析师&#xff0c;年薪轻松突破50万想必同学们看到这个数据并不惊讶&#xff0c;如今在中国&#xff0c;各行各业对数据分析岗位的需求日益提高&#xff1a;在线社交媒体&#xff0c;希望通过数据时刻洞察用户关注…

c语言电脑蓝屏代码,电脑蓝屏代码0x0000001a的解决方法

遇到蓝屏&#xff0c;第一时间记录停机码&#xff0c;如有导致蓝屏的文件名&#xff0c;那么排查的范围就会缩小。下面小编就为大家介绍电脑蓝屏代码0x0000001a的解决方法介绍&#xff0c;希望能对大家有所帮助&#xff01;方法/步骤1、先看这张截图的停机码所在位置。STOP后面…

关于序列建模,是时候抛弃RNN和LSTM了

作者表示&#xff1a;我们已经陷入 RNN、LSTM 和它们变体的坑中很多年&#xff0c;是时候抛弃它们了&#xff01;在 2014 年&#xff0c;RNN 和 LSTM 起死回生。我们都读过 Colah 的博客《Understanding LSTM Networks》和 Karpathy 的对 RNN 的颂歌《The Unreasonable Effecti…

如何运用并行编程Parallel提升任务执行效率

本文来自小易&#xff0c;【DoTNET技术圈】公众号已获得转载授权。《.NET并发变成实战》读后感&#xff1a;并行编程Parallel手打目录&#xff1a;一、前言二、任务并行库&#xff08;TPL&#xff09;的介绍三、Parallel.Invoke的使用四、Parallel.For的使用五、Parallel.ForEa…

通过 GitHub Actions 自动创建 Github Release

通过 GitHub Actions 自动创建 Github ReleaseIntro在 GitHub 上维护了几个小的开源项目&#xff0c;每次在发布新版本的时候会创建一个 release&#xff0c;这样可以比较方便的找到对应的版本的代码&#xff0c;不需要再人肉的从 git log 中找到指定的 commit&#xff0c;而且…

利用win7的applocker功能来有组织的阻止相关软件运行

我有4年没用360相关的软件了&#xff0c;没有360的弹窗用电脑就是舒服。但是我的电脑有时候会有别人来用&#xff0c;我的同学或者我的父母。但是他们经常会自作主张地在我的电脑上装上360卫士&#xff0c;还有360浏览器。每次都是装了我再删&#xff0c;反反复复也不是办法。后…

人工智能会让工作环境变得更公平,还是更压抑?

人工智改变的不仅仅是工作内容……人工智能&#xff08;AI&#xff09;正在踏足商业领域。正如我们本周的特别报道所诠释到的那样&#xff0c;所有类型的公司都在利用人工智能来预测需求&#xff0c;雇佣员工和对付客户。2017年&#xff0c;企业在与人工智能相关的并购交易中支…

离开时请告诉自己生意就是生意

离开时请告诉自己生意就是生意 离职或者被离职&#xff0c;是职场中很常见的事情&#xff0c;每天这个星球上不知道要发生多少次&#xff0c;不管你经历过与否、体会到其中的滋味与否&#xff0c;他都会一直存在着&#xff01;当这一切发生时&#xff0c;不管你是获利者还是受害…

如何假装成一名好程序员?

最近我对一位资深程序员就工作生活问题进行了深度采访&#xff0c;采访内容如下&#xff1a;Q&#xff1a;请问你工作多久了&#xff1f;A&#xff1a;5年了Q&#xff1a;你觉得&#xff0c;程序员这份工作对你有什么影响吗&#xff1f;A&#xff1a;有Q&#xff1a;那你的生活…