浅谈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(‘,‘)[1]);
            Assembly assemblycar = Assembly.Load(car.Split(‘,‘)[1]);
            Type typepeople = assemblypeople.GetType(people.Split(‘,‘)[0]);
            Type typecar = assemblypeople.GetType(car.Split(‘,‘)[0]);
            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.映射依赖关系

时间: 2024-11-01 14:02:21

浅谈IOC的相关文章

浅谈Android保护技术__代码混淆

浅谈Android保护技术__代码混淆 代码混淆 代码混淆(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为.将代码中的各种元素,如变量,函数,类的名字改写成无意义的名字.比如改写成单个字母,或是简短的无意义字母组合,甚至改写成"__"这样的符号,使得阅读的人无法根据名字猜测其用途.对于支持反射的语言,代码混淆有可能与反射发生冲突.代码混淆并不能真正阻止反向工程,只能增大其难度.因此,对于对安全性要求很高的场合,仅仅

蚂蚁变大象:浅谈常规网站是如何从小变大的(八)(转)

原文:http://blog.sina.com.cn/s/blog_6203dcd60100y1vi.html          [第十一阶段 :命名位置服务]   在前面我们不止一次提到了命名位置服务(Naming & Location Service).在不同的架构或者公司里面,这个名字往往不一样,比如,在java里面叫JNDI(Java Naming & Directory Interface),在有些地方可能会叫做资源位置系统(Resource Location System).

1.1浅谈Spring(一个叫春的框架)

如今各种Spring框架甚嚣尘上,但是终归还是属于spring的东西.所以在这里,个人谈一谈对spring的认识,笔者觉得掌握spring原理以及spring所涉及到的设计模式对我们具有极大的帮助.我们基于what ,why ,how来研究Spring. Spring是什么? Spring为什么? 如何使用Spring? 关于这三个问题可以先自行百度!!!针对这个3个问题提出以下几点. Spring有三大地方值得注意: 1.IOC容器 2.IOC控制反转和DI依赖注入 3.AOP面向切面编程 首

.net中对象序列化技术浅谈

.net中对象序列化技术浅谈 2009-03-11 阅读2756评论2 序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储和传输数 据.例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象.反之,反序列化根据流重新构造对象.此外还可以将对象序列化后保存到本地,再次运行的时候可以从本地文件 中“恢复”对象到序列化之前的状态.在.net中有提供了几种序列化的方式:二进制序列化

浅谈——页面静态化

现在互联网发展越来越迅速,对网站的性能要求越来越高,也就是如何应对高并发量.像12306需要应付上亿人同时来抢票,淘宝双十一--所以,如何提高网站的性能,是做网站都需要考虑的. 首先网站性能优化的方面有很多:1,使用缓存,最传统的一级二级缓存:2,将服务和数据库分开,使用不同的服务器,分工更加明确,效率更加高:3,分布式,提供多台服务器,利用反向代理服务器nginx进行反向代理,将请求分散开来:4,数据库的读写分离,不同的数据库,将读操作和写操作分开,并实时同步即可:5,分布式缓存,使用memc

单页应用SEO浅谈

单页应用SEO浅谈 前言 单页应用(Single Page Application)越来越受web开发者欢迎,单页应用的体验可以模拟原生应用,一次开发,多端兼容.单页应用并不是一个全新发明的技术,而是随着互联网的发展,满足用户体验的一种综合技术. SEO 一直以来,搜索引擎优化(SEO)是开发者容易忽略的部分.SEO是针对搜索(Google.百度.雅虎搜索等)在技术细节上的优化,例如语义.搜索关键词与内容相关性.收录量.搜索排名等.SEO也是同行.市场竞争常用的的营销手段.Google.百度的搜

浅谈html标签

浅谈html各常用标签用法 标题标签:<h1>-<h6>来表示,使标题字体变粗. <br />换行标记 <hr />水平分隔符 &nbsp空格符 &copy版权符 <a href>a标签超链接 href可接链接地址 <p>段落标签<blockquote>引用标签及可用做缩进 <table>表格中的<ul>无序列表<ol>有序列表<dl>自定义列表<row

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

[nRF51822] 14、浅谈蓝牙低功耗(BLE)的几种常见的应用场景及架构(科普类干货)

蓝牙在短距离无线通信领域占据举足轻重的地位—— 从手机.平板.PC到车载设备, 到耳机.游戏手柄.音响.电视, 再到手环.电子秤.智能医疗器械(血糖仪.数字血压计.血气计.数字脉搏/心率监视器.数字体温计.耳温枪.皮肤水分计等), 再到智能家居等领域均占有一席之地. 而蓝牙低功耗(BLE)是在蓝牙4.0协议上修改以适用低功耗应用场景的一种蓝牙协议. 随着上一股智能消费类电子大潮的到来,BLE的各种应用也像雨后春笋般在市场上铺开. 如果想 紧跟蓝牙协议的最新动态 ,可以在https://www.b