My.Ioc 代码示例——谈一谈如何实现装饰器模式,兼谈如何扩展 My.Ioc

装饰器模式体现了一种“组合优于继承”的思想。当我们要动态为对象增加新功能时,装饰器模式往往是我们的好帮手。

很多后期出现的 Ioc 容器都为装饰器模式提供了支持,比如说 Autofac。在 My.Ioc 中,默认不提供装饰器支持,但我们可以自己进行扩展,以提供此项功能。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using My.Ioc;
using My.Ioc.Configuration.FluentApi;
using My.Ioc.Configuration.Injection;
using My.Ioc.Core;
using My.Ioc.Dependencies;
using My.Ioc.Injection;

namespace ImplementDecorator
{
    #region Extensions

    public class ReflectionDecoratorConstructorInjector<T> : Injector<T>
    {
        readonly ConstructorInfo _constructorInfo;

        public ReflectionDecoratorConstructorInjector(ConstructorInfo constructorInfo)
        {
            _constructorInfo = constructorInfo;
        }

        public override void Execute(InjectionContext<T> context)
        {
            var instance = _constructorInfo.Invoke(new object[] {context.Instance});
            InjectInstanceIntoContext(context, (T)instance);
        }
    }

    public class ReflectionDecoratorInjectionConfigurationInterpreter : IInjectionConfigurationInterpreter
    {
        readonly TypedInjectionConfigurationGroup _configGroup;

        public ReflectionDecoratorInjectionConfigurationInterpreter(TypedInjectionConfigurationGroup configGroup)
        {
            _configGroup = configGroup;
        }

        public InjectionConfigurationGroup InjectionConfigurationGroup
        {
            get { return _configGroup; }
        }

        public Injector<T> Parse<T>(Kernel kernel, ObjectDescription description, out List<DependencyProvider> groupDepProviders)
        {
            var ctorConfigItem = _configGroup.ConstructorInjectionConfigurationItem;
            groupDepProviders = null;
            return new ReflectionDecoratorConstructorInjector<T>(ctorConfigItem.Constructor);
        }
    }

    public static class DecoratorApi
    {
        public static ICommonConfigurationApi ApplyDecorator(this ICommonConfigurationApi typeConfig, Type decoratedType)
        {
            if (typeConfig == null)
                throw new ArgumentException();
            if (decoratedType == null)
                throw new ArgumentException();

            var configSet = typeConfig.InjectionConfigurationSet;

            var description = configSet.ObjectDescription;
            if (!description.ContractType.IsAssignableFrom(decoratedType))
                throw new ArgumentException();

            var ctor = decoratedType.GetConstructor(new Type[] { typeof(OriginalService) });
            //var parameters = new PositionalParameterSet(Parameter.Positional<Service>());
            //var option = typeConfig.Kernel.ContainerOption;
            //var ctor = option.ConstructorSelector.SelectConstructor(decoratedType, parameters);

            IConstructorInjectionConfigurationItem ctorConfigItem = new ConstructorInjectionConfigurationItem(ctor, null);
            var configGroup = new TypedInjectionConfigurationGroup(description, ctorConfigItem);
            configSet.AddCustomInjectionConfigurationGroup(configGroup);
            configGroup.InjectionConfigurationInterpreter = new ReflectionDecoratorInjectionConfigurationInterpreter(configGroup);
            return typeConfig;
        }
    }

    #endregion

    #region Test Types
    
    public interface IService
    {
        void Execute();
    }

    public class OriginalService : IService
    {
        public void Execute()
        {
            Console.WriteLine("Service execute...");
        }
    }

    public class DecoratedService : IService
    {
        readonly IService _innerService;

        public DecoratedService(IService innerService)
        {
            _innerService = innerService;
        }

        public void Execute()
        {
            Log();
            _innerService.Execute();
        }

        static void Log()
        {
            Console.WriteLine("DecoratedService execute...");
        }
    }

    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            var container = new ObjectContainer(false);
            container.Register<IService, OriginalService>()
                .ApplyDecorator(typeof(DecoratedService))
                .In(Lifetime.Container());

            container.CommitRegistrations();

            var service = container.Resolve<IService>();
            Debug.Assert(service != null);
            Debug.Assert(service is DecoratedService);

            var service2 = container.Resolve<IService>();
            Debug.Assert(service == service2);

            Console.WriteLine("The ImplementDecorator success...");
            Console.ReadLine();
        }
    }
}
View Code

在上面的示例代码中,我们设计了两个服务类:OriginalService 和 DecoratedService,它们共同实现一个契约 IService。其中,OriginalService 就是原始服务类,而 DecoratedService 就是经过装饰之后的服务类,这从命名上也很容易看出来。接下来,我们要介绍的就是如何在 My.Ioc 中给 OriginalService 增加装饰器。

要实现这个目的,首先要了解 My.Ioc 如何构造对象。如果您对 My.Ioc 的内部实现细节不感兴趣,请直接前往这里

My.Ioc 如何构造对象

在这一部分,我们来介绍一下 My.Ioc 的内部对象构造过程。通过下面一张图,可以帮助我们大致了解 My.Ioc 的对象构造过程:

在 My.Ioc 中,当我们调用 container.Resolve 时,容器将为我们完成一系列子对象构造。看到这里,有人可能要问了,为什么是构造一系列子对象而不是构造一个对象呢?这是因为我们预留了这样的可能性(即可扩展性):当我们向容器请求某个服务时,容器返回给我们的不一定是最初注册的对象,而可能是经过某种修饰或增强的、实现了相同契约的对象。

以上面的示例来说,我们最初注册到容器中的服务是 OriginalService,但之后我们为其增加了一个装饰器 DecoratedService,因此容器最后返回给我们的应该是一个 DecoratedService 对象,而不是最初注册的 OriginalService 对象。如此一来,这里要创建的子对象便有两个了:首先需要创建一个 OriginalService 对象,然后以该对象为参数创建一个 DecoratedService 对象。

请注意,我们将容器在最终返回结果之前所创建的一系列中间对象称为子对象,也就是上图中的对象1、对象2、对象3...等等。

在创建每一个子对象时,我们还考虑到可能需要为其公共属性赋予一个属性值(即属性注入),或者调用其公共方法(即方法注入)。因此,容器除了调用子对象构造函数(即构造函数注入)以创建子对象实例之外,可能还要在创建好子对象实例之后调用其成员方法,或者调用其公共属性的 set 方法以便给公共属性赋值。

经过这样分析,上面这张图也不难理解了。当我们在容器中注册某个服务时,如果我们没有指定在创建好对象之后接着调用对象的任何公共方法(方法注入)或属性 set 方法(属性注入),也没有为该服务指定其他装饰或增强类型,那么当我们向容器请求该服务时(调用 container.Resolve),容器将直接调用该服务的构造函数以创建一个服务对象,并将其返回给我们。如果我们在注册时指定了要注入的属性或方法,那么容器除了创建服务对象之外,还会调用指定的公共方法或属性 set 方法。如果我们在注册时,还指定了某些装饰或增强类型,那么容器不但会创建最初的服务对象本身,还会创建指定装饰或增强类型的实例,并且最终将后者返回给我们。

在设计上,我们采用 [配置 + 解释] 的方式来表示 My.Ioc 的对象创建过程。所谓“配置”是指我们将每个子对象的构造(包括构造函数注入、属性注入、方法注入等)分解成一个个配置项,通过组合所有这些配置项来组成一个子对象创建过程。所谓“解释”是指当我们获得每个子对象的所有配置项之后,我们用一个解释程序将这些配置转换成实际的子对象创建器(在 My.Ioc 中对应的对象是 Injector)。最后,当我们拥有一个个子对象创建器之后,我们再将其组装成一个子对象链创建过程。

在实现上,我们采用管道 (Pipeline) 模式来表示整个配置过程。管道中每一个节点对应一个子对象创建配置(在 My.Ioc 框架中,我们称为注入配置组,用 InjectionConfigurationGroup 来表示),所有节点组合到一起就是一个子对象链创建配置(称为注入配置集,用 InjectionConfigurationSet 来表示)。每一个节点(即 InjectionConfigurationGroup)又分为两个子节点:子对象构造函数配置项(对应 IConstructorInjectionConfigurationItem)+ 子对象成员方法和属性 set 方法配置项(对应 IMemberInjectionConfigurationItem)。其中,第一个节点的第一个子节点是必需的,其他部分都是可选的。

有了上面这些知识作为铺垫,接下来要回答如何为 My.Ioc 添加装饰器支持,甚至如何扩展 My.Ioc 的问题,也就不难了。

如何实现装饰器模式

要为服务添加一个装饰器,首先我们必须在服务的配置集 (InjectionConfigurationSet) 中增加一个配置组 (InjectionConfigurationGroup),并为该配置组提供一个构造函数配置项 (IConstructorInjectionConfigurationItem)。此外,我们还需要为该配置组提供一个解释程序(示例代码中的 ReflectionDecoratorInjectionConfigurationInterpreter)。在这个解释程序中,我们需要定义如何将这个配置组转换成一个子对象创建器(在 Parse 方法中定义)。然后,我们便可以将该配置节点添加到服务的配置集中。这样,当我们调用 container.Resolve 方法时,容器便会向我们返回所期望的结果。

添加扩展方法

当实现过程了然之后,我们可以为这个过程编写一个扩展方法,以方便后续使用,如示例代码中所示。

 

本文示例代码以及 My.Ioc 框架源码可在此处获取。

posted @ 2014-09-11 16:44  Johnny.Liu  阅读(323)  评论(0编辑  收藏  举报