本帖最后由 hWonner 于 2012-07-31 05:42:26 编辑

解决方案 »

  1.   

    不错,看了半天才明白... ...
    先从Container.get<StubService>()获取StubService接口实例,
    然后通过service.Repository.ShouldBeOfType<StubRepositoryImpl>()获取StubService接口下的StubRepositoryImpl这个实现类... ...
    不知道理解的对不对... ..
    不过建议楼主代码再规范一点,更好,本文可以作为IOC的入门教科书了... ...
      

  2.   

    1. 整个实现还在连载中, 没有完全。 请看依赖注入框架的C#实现(连载之二)赤裸裸的实现
    2。 我更多的是想描述实现的过程,而不仅仅是结果。
    3。 我想你说的自定义类型,是指Mechine.Specification吧,一则,这不是我的代码的一部分,第二,Machine.Specification (BDD)本身内容太多,你可以Google搜,我就不在这罗嗦了。
      

  3.   

    之二: 赤裸裸的实现
    完整代码
    1。 依赖关系就是使用关系。
    2。依赖关系的解耦,需要用接口查找实现,字典是最好的工具。
    3. 以下就是赤裸裸的实现,不堪如目,但是简单好理解。最主要是可以让测试通过!!!
    先放接口,便于理解:namespace Skight.LightWeb.Domain { public interface Resolver { Dependency get<Dependency>(); } } 这是实现:using System;
    using System.Collections.Generic;namespace Skight.LightWeb.Domain
    {
        public class ResolverImpl:Resolver
        {
            private readonly IDictionary<Type, object> item_resolvers;        public ResolverImpl(IDictionary<Type, object> itemResolvers)
            {
                item_resolvers = itemResolvers;
            }        public Dependency get<Dependency>()
            {
                return (Dependency) item_resolvers[typeof (Dependency)];
            }
        }然后测试也要作出相应的修改。
    using System;
    using System.Collections.Generic;
    using Machine.Specifications;namespace Skight.LightWeb.Domain.Specs
    {
        public class ResolverSpecs
        {
            private Establish context =
                () =>
                    {
                        var dictioary = new Dictionary<Type, object>();
                        dictioary.Add(typeof (MockInterface), new MockImplementaion());
                        subject = new ResolverImpl(dictioary);
                    };       private It Container_get_by_interface_should_return_its_implementation_class =
                () => subject.get<MockInterface>().ShouldBeOfType<MockImplementaion>();        private static ResolverImpl subject;
            private interface MockInterface { }
            private class MockImplementaion : MockInterface { }        
        }   
    }
      

  4.   

    之三: 注册机的引入
    应用SRP原则,拆分为:解析器 Resolver 和注册器 Registration1。 解析器 Resolver负责解析接口.
     resolver.get<MockInterface>().ShouldBeOfType<MockImplementaion>()
    2.注册器,负责把一个类注册为一个接口
    registration.register<MockInterface, MockImplementation>()看起来,注册器先于解析器工作---还没注册,如何解析? 可是,测试驱动却颠倒了这个顺序。我先实现了解析器,因为,如何用才是我第一关心的。更多代码:
    1。 先是,注册机的接口定义,很简单namespace Skight.LightWeb.Domain
    {
        public interface Registration
        {
            void register<Contract, Implementaion>() where Implementaion : Contract, new();
        }
    }2。 测试:
    当用注册机注册一个接口到一个类时,
      这个<接口-类>对,应该被添加到解析字典中
    using System;
    using System.Collections.Generic;
    using Machine.Specifications;namespace Skight.LightWeb.Domain.Specs
    {
        public class When_use_Registration_to_register_an_infterface_to_a_class
        {
            private Establish context =
                () =>
                    {
                        resolver_dictionary = new Dictionary<Type, object>();
                        subject = new RegistrationImpl(resolver_dictionary);
                };        private Because of =
                () => subject.register<MockInterface, MockImplementation>();        private It the_key_value_paire_should_be_added_to_resovler_dictionary =
                () => resolver_dictionary[typeof (MockInterface)].ShouldBeOfType<MockImplementation>();
            
            private static RegistrationImpl subject;
            private static Dictionary<Type, object> resolver_dictionary;        private interface MockInterface{}
            private class MockImplementation:MockInterface {}
        }
    }
    3。 然后是实现:
    using System;
    using System.Collections.Generic;namespace Skight.LightWeb.Domain
    {
        public class RegistrationImpl:Registration
        {
            private IDictionary<Type, object> item_resolvers;
            public RegistrationImpl(IDictionary<Type, object> item_resolvers)
            {
                this.item_resolvers = item_resolvers;
            }        public void register<Contract, Implementaion>() where Implementaion : Contract, new()
            {
                item_resolvers.Add(typeof(Contract), new Implementaion());
            }
        }
    }
      

  5.   

    是的! 简单介绍一下背景:1. 是行为驱动框架,基于已有的测试框架nUnit,用AAA语法重新封装。
    2. AAA: 所有测试分成三部分: 
      1> 测试的组织(建立测试环境) Arrange  
      2> 测试本身, Action
      3> 断言 Assert
    分别对应MSpec术语: Context, Because, It 
    与nUnit不同,MSpec每个测试是一个类,而不是一个方法。这样,测试能够继承等等利用上类的功能,使得测试本身能够设计规划。
    3. Git是一个开源的源代码管理工具,Linux作者写的,比较新,我看好它的底层设计,GitHub.com是用Git搭建的开源代码网站。你不需要下载Git就可以直接下载,有个Zip按钮。 当然,下了Git会更好,还有,如果注册账号,你还可以参与到代码的修改。你可以直接Fork任何其他人的开源项目,就像变成你自己的一样,然后,可以在该基础上,任意修改,就像你自己的项目一样。 最后, 你还可以把你的修改提交给原作者,他可以你的修改合并到他的原项目中去。 这样,为开源的协作提供更好的机制。
      

  6.   

    先从Container.get<StubService>()获取StubService接口实例, ==》这就是对注入框架的调用。
    service.Repository.ShouldBeOfType<StubRepositoryImpl>() ==》 这个是测试,当我手工从Container获得 StubService实例时,这个实例所依赖(使用)的另外一个接口(StubRepository)应该自动注入。
    这里, ShouldBeOfType<StubRepositoryImple>是个断言,翻译成nUnit==> Assert.AreEqual(typeof(StubRepositoryImpl), type(service.Repository));, 
      

  7.   

    继续:
    前文虽繁琐,核心内容却也简单。
    一个字典:
    IDictionary<Type, object> item_resolvers
    一个注册机: public interface Registration
        {
            void register<Contract, Implementaion>() where Implementaion : Contract, new();
        }一个解析器: public interface Resolver
        {
            Dependency get<Dependency>();
        }后两者的构造器,以字典为参数,从而穿在一起。 public RegistrationImpl(IDictionary<Type, DiscreteItemResolver> item_resolvers)
            {
                this.item_resolvers = item_resolvers;
            } public ResolverImpl(IDictionary<Type, DiscreteItemResolver> itemResolvers)
            {
                item_resolvers = itemResolvers;
            }
      

  8.   

    如果你成功的下载源代码,成功的配置好Machine.Specification的测试插件,并成功的运行了测试后,你就会发现,仍然有一个测试没有通过,甚至连测试本身都不完整,这个测试就是递归依赖的解析。
    分析过程不罗嗦,结论是字典的定义产生的瓶颈。
    字典值,原来定义为object,现在改为DiscreteItemResolver, 定义如下,没有巨大的变化,需要取object时,通过调用接口方法resolve()。但是,架构方面的变化是巨大的,这是一个典型的后期绑定。不是在注册时绑定实现的对象,而是在解析的时候!
    namespace Skight.LightWeb.Domain
    {
        public interface DiscreteItemResolver
        {
            object resolve();
        }
    }接下来,根据这个变化清理代码,所有用字典取Value的地方加一个resolve()
    所有改动参看: https://github.com/SkightTeam/LightWeb/commit/61df555f5202907d54a86a6374b164d69a86b4c2
    需要注意的是,原来注册机RegistrationImpl的实现从简单的new 一个对象,改为本地Mock一个DiscreteItemResolver的简单实现。
     item_resolvers.Add(typeof(Contract), new Implementaion());
     item_resolvers.Add(typeof(Contract), new MockResolverImpl<Implementation>());
      

  9.   

    唉,特殊效果不起作用。
     item_resolvers.Add(typeof(Contract), new Implementaion());
     item_resolvers.Add(typeof(Contract), new MockResolverImpl<Implementation>());