zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    [git] push.default is unset
    [daily][qemu][libvirt] 使用libvirt管理qemu
    [development] __attribute__((weak))是干嘛的
    [daily] 使用左右对比查看diff 格式的文件
    [daily] 主机间目录共享
    [daily][qemu][kvm] 使用virtfs在host与guest之间共享目录
    [development][thrift] RPC框架 thrift
    [daily] 像tcpdump一样监听unix domain socket
    [daily][cgroup] 使用cgroup限制进程的CPU占用
    [qemu] qemu旧的net参数已经不再可用了,新的这样用。
  • 原文地址:https://www.cnblogs.com/linybo/p/12083580.html
Copyright © 2011-2022 走看看