Castle Windsor Ioc 一个接口多个实现解决方案

介绍

Castle Windsor 是微软的Ioc类库,本文主要介绍解决一个接口多个实现的解决方案

接口和类

以下内容不是真实的实际场景,仅仅是提供解决一个接口多个实现的思路。

业务场景类

先假设有一接口IHello,该接口提供一个方法SayHello(string name),代码如下:

public interface IHello
{
    void SayHello(string name);
}

这个接口有三个实现类,分别是ShanghaiHello WuxiHelloBeijingHello,代码如下

public class ShanghaiHello : IHello
    {
        public void SayHello(string name)
        {
            Console.WriteLine($"{name},欢迎来到上海");
        }
    }
    
    public class WuxiHello : IHello
    {
        public void SayHello(string name)
        {
            Console.WriteLine($"{name},欢迎来到无锡");
        }
    }
    
    public class BeijingHello : IHello
    {
        public void SayHello(string name)
        {
            Console.WriteLine($"{name},欢迎来到北京");
        }
    }

初始化Ioc容器并且注册 IHello

// 初始化Ioc容器
IWindsorContainer _container= new WindsorContainer();

_container.Register(Component.For<IHello>().ImplementedBy<ShanghaiHello>(),
          Component.For<IHello>().ImplementedBy<WuxiHello>(),
          Component.For<IHello>().ImplementedBy<BeijingHello>());

从容器中获取IHello

var shanghaiHello = _container.Resolve<IHello>();
shanghaiHello.SayHello("武侯");
            
var wuxiHello = _container.Resolve<IHello>();
wuxiHello.SayHello("武侯");

运行结果 


通过运行结果可知,虽然将三个实现类注入到Ioc容器中,但是通过Ioc解析IHello时,发现只能解析到第一条注册的组件,即ShanghaiHello


方案一:使用具体实现类(不推荐)

具体的思路是将实现类作为Windsor的服务注入,虽然该方案可以解决,但是这样做会依赖实现类型。

注册代码

修改注册代码,将具体的实现类作为服务注册

_container.Register(Component.For<IHello,ShanghaiHello>().ImplementedBy<ShanghaiHello>(),
                  Component.For<IHello,WuxiHello>().ImplementedBy<WuxiHello>(),
                  Component.For<IHello,BeijingHello>().ImplementedBy<BeijingHello>());

解析使用代码

            var shanghaiHello = _container.Resolve<ShanghaiHello>();
            shanghaiHello.SayHello("武侯");
            
            var wuxiHello = _container.Resolve<WuxiHello>();
            wuxiHello.SayHello("武侯");
            
            var beijingHello = _container.Resolve<BeijingHello>();
            beijingHello.SayHello("武侯");

运行结果 

 

方案二:扩展接口(推荐)

思路是针对三个实现类分别写接口,继承IHello,接口内容为空

接口

public interface IShanghaiHello : IHello
    {
    }
    
    public interface IWuxiHello : IHello
    {
    }
    
    public interface IBeijingHello : IHello
    {
    }

修改实现

public class ShanghaiHello : IShanghaiHello
    {
        public void SayHello(string name)
        {
            Console.WriteLine($"{name},欢迎来到上海");
        }
    }
    
    public class WuxiHello : IWuxiHello
    {
        public void SayHello(string name)
        {
            Console.WriteLine($"{name},欢迎来到无锡");
        }
    }
    
    public class BeijingHello : IBeijingHello
    {
        public void SayHello(string name)
        {
            Console.WriteLine($"{name},欢迎来到北京");
        }
    }

修改注册方式

_container.Register(Component.For<IHello,IShanghaiHello>().ImplementedBy<ShanghaiHello>(),
                  Component.For<IHello,IWuxiHello>().ImplementedBy<WuxiHello>(),
                  Component.For<IHello,IBeijingHello>().ImplementedBy<BeijingHello>());

使用

      var shanghaiHello = _container.Resolve<IShanghaiHello>();
            shanghaiHello.SayHello("武侯");
            
            var wuxiHello = _container.Resolve<IWuxiHello>();
            wuxiHello.SayHello("武侯");
            
            var beijingHello = _container.Resolve<IBeijingHello>();
            beijingHello.SayHello("武侯");

运行结果

 

通过扩展自定义接口,也可以达到目的


基于方案二,工厂模式实现类(不推荐)

方法2解决了不需要依赖具体实现类的弊端,但是每次客户端调用都需要依赖容器,因此我们可以添加一个工厂,封装Ioc的解析过程

工厂实现

public class HelloFactoryOne
    {
        private readonly IWindsorContainer _container;

        public HelloFactoryOne(IWindsorContainer container)
        {
            this._container = container;
        }

        public IHello Create<T>() where T : IHello
        {
            return this._container.Resolve<T>();
        }
    }

解析调用

      var factoryOne = new HelloFactoryOne(_container);

            var shanghaiHello = factoryOne.Create<IShanghaiHello>();
            shanghaiHello.SayHello("武侯");
            
            var wuxiHello = factoryOne.Create<IWuxiHello>();
            wuxiHello.SayHello("武侯");
            
            var beijingHello = factoryOne.Create<IBeijingHello>();
            beijingHello.SayHello("武侯");

运行结果 

 

基于方案二,工厂模式--接口(推荐)

该方案基于Castle Windsor的AsFactory方法

工厂接口

public interface IHelloFactory
    {
        IHello Create<T>() where T : IHello;
    }

注意:该接口是没有任务实现的,注入的时候,使用AsFactory方式,Windsor会自动帮助我们创建实现

注册工厂接口

 // AsFactory方法必须要加这句
            _container.AddFacility<TypedFactoryFacility>();
            _container.Register(Component.For<IHello,IShanghaiHello>().ImplementedBy<ShanghaiHello>(),
                        Component.For<IHello,IWuxiHello>().ImplementedBy<WuxiHello>(),
                        Component.For<IHello,IBeijingHello>().ImplementedBy<BeijingHello>(),
                        // 注册工厂接口
                        Component.For<IHelloFactory>().AsFactory());

使用

      var factory = _container.Resolve<IHelloFactory>();
            var shanghaiHello1 = factory.Create<IShanghaiHello>();
            shanghaiHello1.SayHello("诸葛亮");
            var wuxiHello1 = factory.Create<IWuxiHello>();
            wuxiHello1.SayHello("诸葛亮");
            var beijingHello1 = factory.Create<IBeijingHello>();
            beijingHello1.SayHello("诸葛亮");

运行结果 

 

方案三:使用Namd结合工厂接口(墙裂推荐)

方案二虽然可以解决一个接口对应多个实现的问题,但是需要扩展接口,如何不扩展接口,又能实现我们的需求呢?答案是注册的时候使用Named,并且解析AsFactory功能

修改注册

主要是给每个注册实例添加Named别称

      // AsFactory方法必须要加这句
            _container.AddFacility<TypedFactoryFacility>();
            _container.Register(Component.For<IHello,IShanghaiHello>().ImplementedBy<ShanghaiHello>().Named("Shanghai"),
                        Component.For<IHello,IWuxiHello>().ImplementedBy<WuxiHello>().Named("Wuxi"),
                        Component.For<IHello,IBeijingHello>().ImplementedBy<BeijingHello>().Named("Beijing"),
                        // 注册工厂接口
                        Component.For<IHelloFactory>().AsFactory());

扩展IHelloFactory接口

给三个实现类添加对应的获取方法

public interface IHelloFactory
    {
        IHello Create<T>() where T : IHello;

        /// <summary>
        /// 方法名必须是 Get[Named]格式
        /// </summary>
        /// <returns></returns>
        IHello GetShanghai();
        
        /// <summary>
        /// 方法名必须是 Get[Named]格式
        /// </summary>
        /// <returns></returns>
        IHello GetWuxi();
        
        /// <summary>
        /// 方法名必须是 Get[Named]格式
        /// </summary>
        /// <returns></returns>
        IHello GetBeijing();
    }

使用

      var factory2 = _container.Resolve<IHelloFactory>();
            var shanghaiHello2 = factory2.GetShanghai();
            shanghaiHello2.SayHello("刘皇叔");
            var wuxiHello2 = factory2.GetWuxi();
            wuxiHello2.SayHello("刘皇叔");
            var beijingHello2 = factory2.GetBeijing();
            beijingHello2.SayHello("刘皇叔");

运行结果 

 

总结

一个接口多个实现在设计中是经常会遇到的问题,如何通过Ioc解决解析的问题一直是我没想明白的问题,最近在工作中也遇到类似的,在使用模板方法 命令 等设计模式的时候,就遇到了一个接口多个实现这个之前一直没解决的内容。
一开始解决这个问题的时候,使用的是具体实现类,在到后来的工厂模式,文中介绍的几种解决方案也是我在解决实习问题中一一尝试的方案。

  1. 直接依赖实现类
  2. 扩展接口
    2.1 具体工厂类
    2.2 抽象工厂类
  3. Named结合抽象工厂类

************转摘:https://www.jianshu.com/p/6154d565c3a3

原文地址:https://www.cnblogs.com/linybo/p/12083580.html