打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ObjectBuilder技术内幕(三)
userphoto

2008.01.16

关注

依赖注入模式

阅读了许多代码是不是感觉有点累?让我们稍稍偏离一下故事主线,轻松一下。本节我们简单的介绍一下近年来十分流行的依赖注入模式。

 

 

 

 

 

 

 

 

其实我们对依赖注入并不陌生,你一直都在不自觉地使用它,无论是ASP.NET还是WinForm的应用,都要用到System.ComponentModel命名空间中的类,如果你比较细心或者你设计过定制控件,你一定注意到IComponent这个接口或者Component这个类,还有IContainerContainer等类,它们的设计就是使用了依赖注入模式。如果你愿意的话,下面我们来写一个小程序,看看到底什么是依赖注入以及它能够解决什么问题。

 

 

 

 

 

 

 

 

首先,创建一个Windows Application项目,打开项目中的Form1,从工具栏拖放一个Label到窗体上,设置Text属性为Hello, World!,编译运行,观察Hello, World!的字体大小,然后退出运行的程序。

 

 

 

 

 

 

 

 

在项目中添加一个类:TestContainer,让它从Container派生。在项目添加对System.Drawing.dll的引用。编写TestContainer的代码如下:

 

 

 

 

 

 

 

 

 class TestContainer : Container

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

    AmbientProperties props;

 

 

 

 

 

 

 

 

    protected override object GetService(Type service)

 

 

 

 

 

 

 

 

    {

 

 

 

 

 

 

 

 

        if (service == typeof(AmbientProperties))

 

 

 

 

 

 

 

 

        {

 

 

 

 

 

 

 

 

            if (props == null)

 

 

 

 

 

 

 

 

            {

 

 

 

 

 

 

 

 

                props = new AmbientProperties();

 

 

 

 

 

 

 

 

                props.Font = new Font("Arial", 16);

 

 

 

 

 

 

 

 

            }

 

 

 

 

 

 

 

 

            return props;

 

 

 

 

 

 

 

 

        }

 

 

 

 

 

 

 

 

        return base.GetService(service);

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 打开Program.cs文件,修改代码如下所示:

 

 

 

 

 

 

 

 

static class Program

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

        static TestContainer testContainer = new TestContainer();

 

 

 

 

 

 

 

 

         [STAThread]

 

 

 

 

 

 

 

 

        static voidMain()

 

 

 

 

 

 

 

 

        {

 

 

 

 

 

 

 

 

            Application.EnableVisualStyles();

 

 

 

 

 

 

 

 

            Application.SetCompatibleTextRenderingDefault(false);

 

 

 

 

 

 

 

 

         Form1 mainForm = new Form1();

 

 

 

 

 

 

 

 

testContainer.Add(mainForm);

 

 

 

 

 

 

 

 

            Application.Run(mainForm);

 

 

 

 

 

 

 

 

        }

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 我们已经对Form1施加了魔法。现在重新运行项目, 注意到没有? Hello,world!的字体大小改变了。

 

 

 

 

 

 

 

 

我们并没有直接对窗体的Label设置字体属性。魔法的秘密就是我们把TestContainer作为Form1的一个容器,在TestContainer容器中,AmbientProperties对象作为一个服务,当Form1Label设置字体属性时,首先查询Label的字体属性设置,如果它找到这样的一个设置,那么就使用它,如果没有找到,它就会向容器(TestContainer)请求,调用容器的GetService来获取必要的服务。

 

 

 

 

 

 

 

 

注意到我们在TestContainerGetService方法中,采用了一个小技巧,就是我们并没有一开始就创建AmbientProperties类实例,只有请求它的时候才去创建。这样会节省系统的资源的使用。GetService的代码有一些问题,试想,如果容器包含的服务不只一个,那么代码中的if语句就会有很多。解决的方法是使用ServiceContainer对象。修改后的代码如下(注意添加必要的引用):

 

 

 

 

 

 

 

 

class TestContainer : Container

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

    ServiceContainer services = new ServiceContainer();

 

 

 

 

 

 

 

 

    public IServiceContainer Services

 

 

 

 

 

 

 

 

    {

 

 

 

 

 

 

 

 

        get { return services; }

 

 

 

 

 

 

 

 

    }

 

 

 

 

 

 

 

 

     protected override object GetService(Type service)

 

 

 

 

 

 

 

 

    {

 

 

 

 

 

 

 

 

        object obj = services.GetService(service);

 

 

 

 

 

 

 

 

        if(obj==null)

 

 

 

 

 

 

 

 

            return base.GetService(service);

 

 

 

 

 

 

 

 

        return obj;

 

 

 

 

 

 

 

 

  }

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 修改ProgramMain方法的代码:

 

 

 

 

 

 

 

 

   [STAThread]

 

 

 

 

 

 

 

 

  static voidMain()

 

 

 

 

 

 

 

 

  {

 

 

 

 

 

 

 

 

      Application.EnableVisualStyles();

 

 

 

 

 

 

 

 

      Application.SetCompatibleTextRenderingDefault(false);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AmbientProperties ambientProperties = new AmbientProperties();

 

 

 

 

 

 

 

 

      ambientProperties.Font = new System.Drawing.Font("Arial", 16);

 

 

 

 

 

 

 

 

      testContainer.Services.AddService(typeof(AmbientProperties), ambientProperties);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Form1 mainForm = new Form1();

 

 

 

 

 

 

 

 

testContainer.Add(mainForm);

 

 

 

 

 

 

 

 

     Application.Run(mainForm);

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 现在我们可以添加任意多的服务,但是,我们前面说的对象按需实例化的优点没有了。实际上.NET Framework已经为我们考虑到了这个问题,提供了IServiceContainer.AddService的一个重载版本:AddService(Type type, ServeceCreatorCallback callback);

 

 

 

 

 

 

 

 

这个重载方法,并不传递对象实例,而是一个回调Delegate,当需要这个对象实例的时候,就会调用回调方法。

 

 

 

 

 

 

 

 

修改后的代码如下:

 

 

 

 

 

 

 

 

 [STAThread]

 

 

 

 

 

 

 

 

static voidMain()

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

    Application.EnableVisualStyles();

 

 

 

 

 

 

 

 

    Application.SetCompatibleTextRenderingDefault(false);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  testContainer.Services.AddService(typeof(AmbientProperties),

 

 

 

 

 

 

 

 

OnCreateService);

 

 

 

 

 

 

 

 

 Form1 mainForm = new Form1();

 

 

 

 

 

 

 

 

testContainer.Add(mainForm);

 

 

 

 

 

 

 

 

     Application.Run(mainForm);

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 private static object OnCreateService(IServiceContainer container,

 

 

 

 

 

 

 

 

Type service)

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

    AmbientProperties ambientProperties = new AmbientProperties();

 

 

 

 

 

 

 

 

    ambientProperties.Font = new System.Drawing.Font("Arial", 16);

 

 

 

 

 

 

 

 

    return ambientProperties;

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 以上例子中,TestContainer是一个容器,任何使用它的应用程序和模块都可以使用它Services.AddService来注册一个服务,使用GetService来获取一个服务。这种模式就是依赖注入模式。如果你还不能完全理解依赖注入模式的话,可以阅读Martin Fowler的文章

 

 

 

 

 

 

 

 

依赖注入有几种方式,一种是构造器参数注入,也就是说在创建对象的时候,通过参数构造器把对象依赖的对象作为参数传递;第二种是属性设置器注入,也就是说在通过在对象的相应属性的Setter来传递依赖对象;第三种方法就是通过方法调用来传递。

 

 

 

 

 

 

 

 

OB为此三种注入特意设计了对应的Attribute,通过Attribute来描述对象之间的依赖,这样在创建对象的时候可以使用反射机制实现依赖注入模式,这些Attribute是:

 

 

 

 

 

 

 

 

 public abstract class ParameterAttribute : Attribute

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

       protected ParameterAttribute() { }

 

 

 

 

 

 

 

 

   //方针创建一个参数时可以访问的方法实现,后面再详细介绍

 

 

 

 

 

 

 

 

       public abstract IParameter CreateParameter(Type memberType);

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]

 

 

 

 

 

 

 

 

public sealed class DependencyAttribute : ParameterAttribute

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

       private string name;

 

 

 

 

 

 

 

 

       private Type createType;

 

 

 

 

 

 

 

 

       private NotPresentBehavior notPresentBehavior = NotPresentBehavior.CreateNew;

 

 

 

 

 

 

 

 

       private SearchMode searchMode;

 

 

 

 

 

 

 

 

       public DependencyAttribute()

 

 

 

 

 

 

 

 

       {

 

 

 

 

 

 

 

 

       }

 

 

 

 

 

 

 

 

        //所要注入的对象名称。可选

 

 

 

 

 

 

 

 

       public string Name

 

 

 

 

 

 

 

 

       {

 

 

 

 

 

 

 

 

              get { return name; }

 

 

 

 

 

 

 

 

              set { name = value; }

 

 

 

 

 

 

 

 

       }

 

 

 

 

 

 

 

 

    //当依赖的对象没有找到,如果指定创建一个新的依赖对象,指定它的类型。可选

 

 

 

 

 

 

 

 

       public Type CreateType

 

 

 

 

 

 

 

 

       {

 

 

 

 

 

 

 

 

              get { return createType; }

 

 

 

 

 

 

 

 

              set { createType = value; }

 

 

 

 

 

 

 

 

       }

 

 

 

 

 

 

 

 

        //指定在定位器中搜索对象的模式

 

 

 

 

 

 

 

 

       public SearchMode SearchMode

 

 

 

 

 

 

 

 

       {

 

 

 

 

 

 

 

 

              get { return searchMode; }

 

 

 

 

 

 

 

 

              set { searchMode = value; }

 

 

 

 

 

 

 

 

       }

 

 

 

 

 

 

 

 

        //依赖对象为找到时的行为,默认为CreateNew

 

 

 

 

 

 

 

 

       public NotPresentBehavior NotPresentBehavior

 

 

 

 

 

 

 

 

       {

 

 

 

 

 

 

 

 

              get { return notPresentBehavior; }

 

 

 

 

 

 

 

 

              set { notPresentBehavior = value; }

 

 

 

 

 

 

 

 

       }

 

 

 

 

 

 

 

 

     //创建需要的参数

 

 

 

 

 

 

 

 

       public override IParameter CreateParameter(Type annotatedMemberType)

 

 

 

 

 

 

 

 

       {

 

 

 

 

 

 

 

 

              return new DependencyParameter(annotatedMemberType, name, createType, notPresentBehavior, searchMode);

 

 

 

 

 

 

 

 

       }

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 对象为找到时的行为定义:

 

 

 

 

 

 

 

 

 public enum NotPresentBehavior

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

              CreateNew, //创建该对象

 

 

 

 

 

 

 

 

               ReturnNull, //返回null

 

 

 

 

 

 

 

 

               Throw, //抛出一个以来对象丢失异常

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 当存在多个构造器时,用下面的特性表示被注入使用的构造器:

 

 

 

 

 

 

 

 

 [AttributeUsage(AttributeTargets.Constructor)]

 

 

 

 

 

 

 

 

public class InjectionConstructorAttribute : Attribute

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 实施到一个被注入时必须调用的方法上面

 

 

 

 

 

 

 

 

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

 

 

 

 

 

 

 

 

public sealed class InjectionMethodAttribute : Attribute

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

}

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
节省XP使用内存,加快开机速度
getSystemService() in Android
Bluetooth Smart for Android
Binder机制,从Java到C (3. ServiceManager in Java)
Firefox扩展中Keyconfig配置快捷键常用代码
Android AudioManager音量控制流程
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服