浅谈IOC
一、引言
IOC-Invertion of Control,即控制反转,是一种程序设计思想,世上本没有路,走的人多了便有了路,本文将一步步带你了解IOC设计思想的演进之路。
在学习IOC之前我们先初步了解几个概念
依赖(Dependency):就是有联系,表示一个类依赖于另一个类
依赖倒置原则(DIP):设计模式六大原则之一,是一种软件架构设计原则
控制反转(IOC):一种软件设计原则,上层对下层的依赖(即底层模块的获得)交给第三方
依赖注入(DI):实现IOC的一种方式、手段
IOC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期
二、依赖
依赖就是有联系,有地方使用它就是有依赖它,下面看一个简单的示例
class BMW
{
public string Show()
{
return "宝马";
}
}
class ChinesePeople
{
private BMW bmw = new BMW();
public void Run()
{
Console.WriteLine($"今天开{bmw.Show()}上班");
}
}
class Program
{
static void Main(string[] args)
{
ChinesePeople people = new ChinesePeople();
BMW bmw = new BMW();
people.Run();
Console.Read();
}
}
上面中国人开着宝马去上班,客户端有使用中国人、宝马汽车两个对象,中国人中有使用对象宝马汽车,我们可以从中找到三个依赖关系:
客户端依赖对象ChinesePeople;
客户端依赖对象BMW;
ChinesePeople依赖对象BMW;
三、依赖倒置原则
过些日子来了新需求,中国人不仅要开宝马去上班,还要开奔驰去上班,如果按照上面直接依赖关系的方式去做,我们就需要修改ChinesePeople类,让它实现一个参数为宝马的重载方法Run(),显然这样不是好的设计,我们总不能每次新增一种汽车(即修改下层模块)都要去修改ChinesePeople类吧(相对于汽车为上层模块),太麻烦了。。。
先简单分析一下,耦合关系就是依赖关系,如果依赖关系很重,牵一发而动全身,将很难维护扩展,耦合关系越少,系统会越稳定,因此要较少依赖
定义:A.高层模块不应依赖于底层模块,两者应该依赖于抽象
B.抽象不应该依赖于细节,细节应该依赖于抽象
在这个图中,我们发现高层模块定义接口,将不直接依赖于下层模块,下层模块负责实现高层模块定义的接口,下面看代码demo:
interface ICar
{
string Show();
}
class BMW:ICar
{
public string Show()
{
return "宝马";
}
}
class BenZ : ICar
{
public string Show()
{
return "奔驰";
}
}
interface IPeople
{
void Run(ICar bmw);
}
class ChinesePeople :IPeople
{
public void Run(ICar bmw)
{
Console.WriteLine($"今天开{bmw.Show()}上班");
}
}
class Program
{
static void Main(string[] args)
{
ICar carBMW = new BMW();
ICar carBenZ = new BenZ();
IPeople people = new ChinesePeople();
people.Run(carBMW);
people.Run(carBenZ);
Console.Read();
}
}
分析:上面代码中,ChinesePeople类不再依赖于具体的汽车,而是依赖于汽车的抽象,这样使得不管换什么样的汽车品牌,中国人都是可以开着去上班的,而且不需要修改ChinesePeople类。想一下,这样是不是挺好的,我们可以得出:上层不再依赖细节,相比面向实现,面向接口较好,因为抽象相比细节要更稳定。
四、控制反转
上面示例中,我们实现了具体的人和具体的汽车的隔离,具体人只和汽车的接口有关。但是Program中main方法里的具体对象写死了,控制权变小,当我要修改美国人开着福特去上班时,就不得不要去修改代码,那怎么把控制权转移呢?
下面看一个简单的示例:
interface ICar
{
string Show();
}
class BMW:ICar
{
public string Show()
{
return "宝马";
}
}
interface IPeople
{
void Run(ICar bmw);
}
class ChinesePeople :IPeople
{
public void Run(ICar bmw)
{
Console.WriteLine($"今天开{bmw.Show()}上班");
}
}
class Program
{
static void Main(string[] args)
{
string people = ConfigurationManager.AppSettings["people"];
string car = ConfigurationManager.AppSettings["car"];
Assembly assemblypeople = Assembly.Load(people.Split(',')[]);
Assembly assemblycar = Assembly.Load(car.Split(',')[]);
Type typepeople = assemblypeople.GetType(people.Split(',')[]);
Type typecar = assemblypeople.GetType(car.Split(',')[]);
IPeople ipeople= (IPeople)Activator.CreateInstance(typepeople);
ICar icar = (ICar)Activator.CreateInstance(typecar);
ipeople.Run(icar);
Console.Read();
}
} <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="people" value="MyIOC_IOC.ChinesePeople,MyIOC_IOC"/>
<add key="car" value="MyIOC_IOC.BMW,MyIOC_IOC"/>
</appSettings>
</configuration>
上面代码中,我们使用反射+配置文件的方式,将对象创建的控制权转移到了配置文件,这就是所谓的控制反转
分析,控制反转是将对象创建的控制权交给了第三方,可以是IOC容器,它就相当于工厂,我们要什么对象,工厂给我们什么对象,这样依赖关系就变了,它们(人和车)都依赖于IOC容器,通过IOC容器建立它们之间的依赖关系。(依赖对象不再被依赖模块的类中直接通过new来获取)
五、依赖注入
上面说到的控制反转,我们了解到是将控制权转移,这是我们的目的,配置文件+反射是是一种实现,而依赖注入则提供的是一种思想,或者说是实现IOC的手段。
依赖注入是将对象的创建和绑定转移到被依赖对象的外部来实现。在依赖关系中ChinesePeople类所依赖的对象BMW类的创建和绑定是在ChinesePeople类内部执行的,显然这种方法是不可取的,那我们怎么BMW类的引用传递给ChinesePeople类呢?
方法一 构造函数注入
interface ICar
{
string Show();
}
class BMW:ICar
{
public string Show()
{
return "宝马";
}
}
class ChinesePeopleContructor
{
private ICar _car;
public ChinesePeopleContructor(ICar bmw)
{
_car = bmw;
}
public void Run()
{
Console.WriteLine($"今天开{_car.Show()}上班");
}
}
static void Main(string[] args)
{
ICar car = new BMW();
ChinesePeopleContructor people = new ChinesePeopleContructor(car);
people.Run();
Console.Read();
}
分析,BMW类对象的创建和绑定转移到ChinesePeople类的外部来实现,解除了两个对象之间的耦合,当需要开奔驰去上班的时候,只需要定义一个奔驰类,外部重新绑定依赖,不需要修改ChinesePeople类的内部,即可是先中国人开奔驰去上班的需求
方法二 属性注入
interface ICar
{
string Show();
}
class BMW:ICar
{
public string Show()
{
return "宝马";
}
}
class ChinesePeopleProperty
{
private ICar _ICar;
public ICar IC
{
get { return _ICar; }
set { _ICar = value; }
}
public void Run()
{
Console.WriteLine($"今天开{_ICar.Show()}上班");
}
}
static void Main(string[] args)
{
ICar car = new BMW();
ChinesePeopleProperty people = new ChinesePeopleProperty();
people.IC = car;
people.Run();
Console.Read();
}
分析,属性注入是通过给属性赋值,从而传递依赖
方法三 接口注入
interface ICar
{
string Show();
}
class BMW:ICar
{
public string Show()
{
return "宝马";
}
}
interface IDependent
{
void SetDependent(ICar icar);
}
class ChinesePeopleInterface : IDependent
{
private ICar _ICar;
public void SetDependent(ICar icar)
{
_ICar = icar;
}
public void Run()
{
Console.WriteLine($"今天开{_ICar.Show()}上班");
}
}
static void Main(string[] args)
{
ICar car = new BMW();
ChinesePeopleInterface people = new ChinesePeopleInterface();
people.SetDependent(car);
people.Run();
Console.Read();
}
分析,接口依赖是定义一个设置依赖的方法,然后被依赖类继承并实现这个接口
六、IOC容器
IOC容器是一个DI框架,主要功能有一下几点
1.动态创建、注入依赖对象;
2.管理对象生命周期
2.映射依赖关系
常见的IOC容器:Spring.NET,Castle Windsor, Ninject,Autofac,Unity等等。。。
ioc容器提供了很多丰富的API,由于时间和篇幅等关系,我会在下篇博客中和您一起学习IOC容器之一Unity,敬请期待,未完待续。。。
不努力一把,坐井观天,将永远不知道自己和别人的差距有多大,身为菜鸟的我相信,天道酬勤,大道至简,最好的成功之道便是坚持、学习、总结。
本文版权归作者和博客园共有,欢迎转载,转载请注明出处。感谢您的阅读。
浅谈IOC的更多相关文章
- 浅谈(IOC)依赖注入与控制反转(DI)
前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...
- [转帖]浅谈IOC--说清楚IOC是什么
浅谈IOC--说清楚IOC是什么 Need Study https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html 博文目录 1. ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 浅谈对Spring Framework的认识
Spring Framework,作为一个应用框架,官方的介绍如下: The Spring Framework provides a comprehensive programming and con ...
- 1.1浅谈Spring(一个叫春的框架)
如今各种Spring框架甚嚣尘上,但是终归还是属于spring的东西.所以在这里,个人谈一谈对spring的认识,笔者觉得掌握spring原理以及spring所涉及到的设计模式对我们具有极大的帮助.我 ...
- 浅谈Spring的两种配置容器
浅谈Spring的两种配置容器 原文:https://www.jb51.net/article/126295.htm 更新时间:2017年10月20日 08:44:41 作者:黄小鱼ZZZ ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
- 浅谈Android保护技术__代码混淆
浅谈Android保护技术__代码混淆 代码混淆 代码混淆(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为.将代码中的各种元 ...
- laravle6.0-IOC-DI浅谈
1.什么是IOC,DI IOC(Inversion of Control)控制反转:ioc意味着,你将自己设计好的对象交给容器来控制,而不是传统的在你的对象内部直接控制.比如: 人 操控 手机 做一些 ...
随机推荐
- 初次尝试Linux并记录一二
假如我有一个Linux系统 安装过程:加载中... 版本:Ubuntu Server 16.04.1 LTS 64位 得到一个IP:*.*.*.* 下载工具 WinSCP: WinSCP是一个Wind ...
- 用kotlin方式打开《第一行代码:Android》
参考:<第一行代码:Android>第2版--郭霖 注1:本文为原创,例子可参考郭前辈著作:<第一行代码:Android> 注2:本文不赘述android开发的基本理论,不介绍 ...
- 取代netcat
前言 众所周知,netcat是网络界的瑞士军刀,它的主要作用是:提供连接其他终端的方法,可以上传文件,反弹shell等等各种利于别人控制你电脑的操作.所以聪明的系统管理员会将它从系统中移除,这样当别人 ...
- 我和 flow.ci 的第一次亲密接触
编者按:本文转载自 flow.ci 用户 @君赏 的实践分享,原文链接这里. 这不是第一次听说 flow.ci ,记得当时 fir.im 新出这个服务的时候,我也是心情十分激动的去尝试,结果是只支持安 ...
- iOS 开发之 protocol Buffer 数据交换
前言: 从 14 年公司做项目时开始接触 Google 的 protocol Buffer,用了一段时间,后来到新公司就没有机会再使用了,趁着还没完全忘记,记录下. 简介:protocolbuffer ...
- Adobe After Effect CC2017 for Mac
前段时间学习使用 Lottie 制作炫酷的动画,需要 Adobe After Effect 安装 bodymovin 插件,然后导出动画的 json 文件.尝试安装 AE 工具.网上查找 史蒂芬周的博 ...
- 使用ConcurrentDictionary替代Hashtable对多线程的对象缓存处理
在之前一段时间里面,我的基类多数使用lock和Hashtable组合实现多线程内缓存的冲突处理,不过有时候使用这两个搭配并不尽如人意,偶尔还是出现了集合已经加入的异常,对代码做多方的处理后依然如故,最 ...
- Mysql数据库二进制安装
MySQL数据库有四种安装方法: 源码包编译安装 RPM包安装 二进制文件安装 官方yum源安装 这里我们主要介绍二进制包的安装方法 在MySQL官网下载二进制包并且上传到服务器上 解压二进制包 [r ...
- SQL SERVER大话存储结构(6)_数据库数据文件
数据库文件有两大类:数据文件跟日志文件,每一个数据库至少各有一个数据文件或者日志文件,数据文件用来存储数据,日志文件用来存储数据库的事务修改情况,可用于恢复数据库使用. 这里分 ...
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...