星辰.Net技术社区论坛

首页 » .NET » 设计模式GOF » .Net中的依赖注入模式
star65225692 - 2008-6-17 11:06:00
其实我们对依赖注入并不陌生,你一直都在不自觉地使用它,无论是ASP.NET还是WinForm的应用,都要用到System.ComponentModel命名空间中的类,如果你比较细心或者你设计过定制控件,你一定注意到IComponent这个接口或者Component这个类,还有IContainerContainer等类,它们的设计就是使用了依赖注入模式。如果你愿意的话,下面我们来写一个小程序,看看到底什么是依赖注入以及它能够解决什么问题。:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
首先,创建一个Windows Application项目,打开项目中的Form1,从工具栏拖放一个Label到窗体上,设置Text属性为Hello, World!,编译运行,观察Hello, World!的字体大小,然后退出运行的程序。:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
在项目中添加一个类:TestContainer,让它从Container派生。在项目添加对System.Drawing.dll的引用。编写TestContainer的代码如下: :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
class TestContainer : Container :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
    AmbientProperties props; :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
   
protected override object GetService(Type service) :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
   
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
if (service == typeof(AmbientProperties)) :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
if (props == null) :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
                props
= new AmbientProperties(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
                props.Font
= new Font("Arial", 16); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
return props; :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
        }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
return base.GetService(service); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
}
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
}
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
打开Program.cs文件,修改代码如下所示::“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
static class Program :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
static TestContainer testContainer = new TestContainer(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
        [STAThread] :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
static void Main() :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Application.EnableVisualStyles(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Application.SetCompatibleTextRenderingDefault(
false);  :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Form1 mainForm
= new Form1(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            testContainer.Add(mainForm); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Application.Run(mainForm); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
        }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
}
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
我们已经对Form1施加了魔法。现在重新运行项目, 注意到没有? Hello,world!的字体大小改变了。:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
我们并没有直接对窗体的Label设置字体属性。魔法的秘密就是我们把TestContainer作为Form1的一个容器,在TestContainer容器中,AmbientProperties对象作为一个服务,当Form1Label设置字体属性时,首先查询Label的字体属性设置,如果它找到这样的一个设置,那么就使用它,如果没有找到,它就会向容器(TestContainer)请求,调用容器的GetService来获取必要的服务。:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
注意到我们在TestContainerGetService方法中,采用了一个小技巧,就是我们并没有一开始就创建AmbientProperties类实例,只有请求它的时候才去创建。这样会节省系统的资源的使用。GetService的代码有一些问题,试想,如果容器包含的服务不只一个,那么代码中的if语句就会有很多。解决的方法是使用ServiceContainer对象。修改后的代码如下(注意添加必要的引用): :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
class TestContainer : Container :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
    ServiceContainer services
= new ServiceContainer(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
   
public IServiceContainer Services :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
   
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
get { return services; } :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
    }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
   
protected override object GetService(Type service) :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
   
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
object obj = services.GetService(service); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
if(obj==null) :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
return base.GetService(service); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
return obj; :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
  }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
}
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
//修改Program的Main方法的代码: :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
  [STAThread] :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
 
static void Main() :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
 
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      Application.EnableVisualStyles(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      Application.SetCompatibleTextRenderingDefault(
false);  :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      AmbientProperties ambientProperties
= new AmbientProperties(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      ambientProperties.Font
= new System.Drawing.Font("Arial", 16); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      testContainer.Services.AddService(
typeof(AmbientProperties), ambientProperties); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      Form1 mainForm
= new Form1(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      testContainer.Add(mainForm); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
      Application.Run(mainForm); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
}
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
    现在我们可以添加任意多的服务,但是,我们前面说的对象按需实例化的优点没有了。实际上.NET Framework已经为我们考虑到了这个问题,提供了IServiceContainer.AddService的一个重载版本:AddService(Type type, ServeceCreatorCallback callback); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
这个重载方法,并不传递对象实例,而是一个回调Delegate,当需要这个对象实例的时候,就会调用回调方法。:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
修改后的代码如下: :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
[STAThread] :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
static void Main() :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Application.EnableVisualStyles(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Application.SetCompatibleTextRenderingDefault(
false); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
//AmbientProperties ambientProperties = new AmbientProperties(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
//ambientProperties.Font = new System.Drawing.Font("Arial", 16); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            testContainer.Services.AddService(typeof(AmbientProperties), OnCreateService); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Form1 mainForm
= new Form1(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            testContainer.Add(mainForm); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            Application.Run(mainForm); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
        }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
private static object OnCreateService(IServiceContainer container,Type service) :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
       
{ :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            AmbientProperties ambientProperties
= new AmbientProperties(); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
            ambientProperties.Font
= new System.Drawing.Font("Arial", 16); :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
           
return ambientProperties; :“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
        }
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
以上例子中,TestContainer是一个容器,任何使用它的应用程序和模块都可以使用它Services.AddService来注册一个服务,使用GetService来获取一个服务。这种模式就是依赖注入模式。如果你还不能完全理解依赖注入模式的话,可以阅读Martin Fowler的文章:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
依赖注入有几种方式,一种是构造器参数注入,也就是说在创建对象的时候,通过参数构造器把对象依赖的对象作为参数传递;第二种是属性设置器注入,也就是说在通过在对象的相应属性的Setter来传递依赖对象;第三种方法就是通过方法调用来传递。:“ÁAî%÷î̊www.netcsharp.cn­«”1IècQ¹
1
查看完整版本: .Net中的依赖注入模式