• 控制反转-Ioc之Unity


         本篇幅主要介绍控制反转的一些概念,和如何使用Unity实现Ioc。在介绍的时候,会尽量结合代码来讲解一些概念。

    1.什么是DI?

         DI即控制反转,是将对具体实现类的依赖转变为对接口的依赖,这样在编程中,就可以发挥类的多态性。我们先假设一台印钞机,功能是打印钞票,根据使用的模板,可以印人民币(想到这里,我做梦都乐了)。具体实现如下代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace IocWithUnity
    {
        /// <summary>
        /// 印钞机
        /// </summary>
        public class CashMachine
        {
            public CashMachine() { }
    
            public void Print()
            {
                CNYCashTemplate template = new CNYCashTemplate();
    
                string templateContent = template.GetTemplate();
    
                System.Console.WriteLine(templateContent);
            }
        }
        /// <summary>
        /// 人民币钞票模板
        /// </summary>
        public class CNYCashTemplate
        {
            public CNYCashTemplate() { }
    
            public string GetTemplate()
            {
                return "这是人民币模板";
            }
        }
    }

        是不是很爽?可以印很多RMB了。哈哈哈哈!!!可是有一天,我们的机器要卖去美国,印美钞,怎么办,于是,我们加了一个美钞模板。代码如下:

    /// <summary>
        /// 美钞钞票模板
        /// </summary>
        public class USDCashTemplate
        {
            public USDCashTemplate() { }
    
            public string GetTemplate()
            {
                return "This is US Dollor.";
            }
        }

        这下尴尬了,我们还得再建一个USDCashMachine。同样的代码,再写一遍。单是优秀的程序猿具有一个良好的特质--懒,所以我们打算改一下,成下面那样:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace IocWithUnity
    {
        /// <summary>
        /// 印钞机
        /// </summary>
        public class CashMachine
        {
            public CashMachine() { }
    
            public void Print(ICashTemplate template)
            {
                string templateContent = template.GetTemplate();
    
                System.Console.WriteLine(templateContent);
            }
        }
        /// <summary>
        /// 印钞模块
        /// </summary>
        public interface ICashTemplate
        {
            /// <summary>
            /// 获取钞票模板
            /// </summary>
            /// <returns></returns>
            string GetTemplate();
        }
    
        /// <summary>
        /// 人民币钞票模板
        /// </summary> 
        public class CNYCashTemplate : ICashTemplate
        {
            public CNYCashTemplate() { }
    
            public string GetTemplate()
            {
                return "这是人民币模板";
            }
        }
        /// <summary>
        /// 美钞钞票模板
        /// </summary>
        public class USDCashTemplate : ICashTemplate
        {
            public USDCashTemplate() { }
    
            public string GetTemplate()
            {
                return "This is US Dollor.";
            }
        }
    }

    我们在调用的时候就变成了

    ICashTemplate template = new USDCashTemplate();
    new CashMachine().Print(template);

        这个时候,我们想印美钞,就放美钞的模板,想印人民币,就印人民币的模板,厉害了吧?

        没错,这就是面向接口的依赖反转,我们的CashMachine从依赖CNYCashTemplate这个具体实现,变成了对ICashTemplate接口的依赖,在上面我们采用的是方法的注入,我们也可以用构造函数注入,用属性注入,思路都是一样的。这就是为什么我们一直强调

    面向接口编程,因为面向接口编程可以增强代码结构的稳定性和可扩展性。

    2.什么是Ioc?

       上面我们的印钞机已经可以印各种钞票了。那么我们在实际开发当中,如果你进行了分层,想必应该是这样的:

    Cash.Business---业务层
    Cash.Templates---钞票模板实现
    Cash.IContract--接口层

        那么这三层的依赖关系应该是

       作为一个有代码洁癖的猿,肯定是不想有那么多复杂的关系的。业界有一句著名的话怎么说来着,没有加一层解决不了的事情,如果有,那么就加俩层。

     

        变成这样之后,是不是感觉简洁很多了,没有错Infrustructure框架层做的事情就是这个,我们将创建具体对象的工作交给了框架,从此以后,CashBusiness的依赖关系就稳定了,我们也过上了衣食无忧,逍遥快乐的日子。

    3.Unity的基本使用

    上面Infrustructure的功能,我们使用的就是Unity。

    public static class IocContainer
        {
            private static IUnityContainer _container = null;
            static IocContainer()
            {
                _container = new UnityContainer();
            }
            /// <summary>
            /// 注册一个实例作为T的类型
            /// </summary>
            /// <typeparam name="T">需要注册的类型</typeparam>
            /// <param name="instance">需要注册的实例</param>
            public static void Register<T>(T instance)
            {
                _container.RegisterInstance<T>(instance);
            }
            /// <summary>
            /// 注册一个名为name的T类型的实例
            /// </summary>
            /// <typeparam name="T">需要注册的类型</typeparam>
            /// <param name="name">关键字名称</param>
            /// <param name="instance">实例</param>
            public static void Register<T>(string name, T instance)
            {
                _container.RegisterInstance(name, instance);
            }
            /// <summary>
            /// 将类型TFrom注册为类型TTo
            /// </summary>
            /// <typeparam name="TFrom"></typeparam>
            /// <typeparam name="TTo"></typeparam>
            public static void Register<TFrom, TTo>() where TTo : TFrom
            {
                _container.RegisterType<TFrom, TTo>();
            }
            /// <summary>
            /// 将类型TFrom注册为类型TTo
            /// </summary>
            /// <typeparam name="TFrom"></typeparam>
            /// <typeparam name="TTo"></typeparam>
            /// <typeparam name="lifetime"></typeparam>
            public static void Register<TFrom, TTo>(LifetimeManager lifetime) where TTo : TFrom
            {
                _container.RegisterType<TFrom, TTo>(lifetime);
            }
            /// <summary>
            /// 将类型TFrom注册名为name类型TTo
            /// </summary>
            /// <typeparam name="TFrom"></typeparam>
            /// <typeparam name="TTo"></typeparam>
            public static void Register<TFrom, TTo>(string name) where TTo : TFrom
            {
                _container.RegisterType<TFrom, TTo>(name);
            }
            /// <summary>
            /// 通过关键字name来获取一个实例对象
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="name"></param>
            /// <returns></returns>
            public static T Resolve<T>(string name)
            {
                return _container.Resolve<T>(name);
            }
            /// <summary>
            /// 获取一个为T类型的对象
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <returns></returns>
            public static T Resolve<T>()
            {
                return _container.Resolve<T>();
            }
            /// <summary>
            /// 获取所有注册类型为T的对象实例
            /// </summary>
            /// <typeparam name="T">需要获取的类型的对象</typeparam>
            /// <returns></returns>
            public static IEnumerable<T> ResolveAll<T>()
            {
                return _container.ResolveAll<T>();
            }
        }

    上面代码中Register就是将对象或实现类,注册到Ioc容器中,在需要使用的地方再调用Resolve获取对象即可,这样,无论我们在哪里需要,都可以用Ioc容器来获取对象,而不再需要使用new来创建对象了。

    4.使用配置文件配置注入

        但是,我们显然不满足于这样,我们还想把实现,彻彻底底的从代码中移除,这里我们就可以借助配置文件来实现了。首先,我们在IocContainer里添加一个静态构造函数,让程序在初次使用IocContainer时加载配置,

    static IocContainer()
            {
                _container = new UnityContainer();
    
                object unitySection = ConfigurationManager.GetSection("unity");
                if (unitySection == null) return;
    
                UnityConfigurationSection section = (UnityConfigurationSection)unitySection;
                section.Configure(_container, "Default");
            }
            /// <summary>
            /// 从文件中加载Unity注入的对象和映射关系等
            /// </summary>
            /// <param name="configFile">Unity容器配置文件的路径</param>
            public static void LoadUnityConfig(string configFile)
            {
                string filePath = configFile;
                var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = filePath };
    
                Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
                UnityConfigurationSection unitySection = (UnityConfigurationSection)configuration.GetSection("unity");
                foreach (var item in unitySection.Containers)
                {
                    _container.LoadConfiguration(unitySection, item.Name);
                }
            }

    web.config(WEB项目是在web.config)里配置我们配置文件的路径,在configuration节点中添加如下配置

    <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
      </configSections>
      <unity configSource="unity.config"/>

    接下来我们来配置我们的unity.config文件(这里unity.config是放在运行目录下的,WEB网站下应该是与bin目录同级)

    <?xml version="1.0" encoding="utf-8" ?>
    <unity xmlns= "http://schemas.microsoft.com/practices/2010/unity ">
      <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>
      <!--注入对象-->
      <typeAliases>
        <!--表示单例-->
        <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,Microsoft.Practices.Unity" />
        <!--表示每次使用都进行创建-->
        <typeAlias alias="transient" type="Microsoft.Practices.Unity.TransientLifetimeManager,Microsoft.Practices.Unity" />
      </typeAliases>
      <container name= "Default">
        <!--配置sql-->
    <!--<register type= "接口类,接口dll" mapTo= "实现类,实现dll"  name="实例名">-->
    <register type= "MJD.Framework.Sql.Interfaces.IConnectionFactory,MJD.Framework.Sql" mapTo= "MJD.Framework.Sql.Oracle.ConnectionFactory,MJD.Framework.Sql.Oracle"> <lifetime type="singleton" /> </register> </container> </unity>

         这样,我们就可以将实现彻底的解耦出来了。怎么样,是不是很酷?以后再也不需要再去更改代码了,直接配置就可以了。

    5.三种生命周期

         在上面的配置中,眼尖的你可能会发现,在register下还配置了一个lifetime,type填写的是一个别名。这里就是所谓的生命周期,在Unity中有三种生命周期

    ContainerControlledLifetimeManager,即单例,生命周期与容器的生命周期一样,一般如果我们使用静态的容器,那么这个就等同于我们的单例模式;
    TransientLifetimeManager,临时的,即每次创建容器都会new一个对象给我们使用;

    HierarchicalLifetimeManager,这个用得比较少,假如容器有分层,有子容器,那么父容器与子容器中的对象都是单例的,但是子类与父类里的对象不是同一个;
  • 相关阅读:
    wifite+aimon-ng
    DC-2
    chrome插件开发
    mongoose的基本操作方法
    webpack中的require.context
    sequelize 数据类型 model
    React17 系统精讲 结合TS打造旅游电商平台
    2021必修 React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目
    21.8.2
    胡渊鸣《浅析信息学竞赛中概率论的基础与应用》学习笔记
  • 原文地址:https://www.cnblogs.com/flyingaway/p/8078463.html
Copyright © 2020-2023  润新知