SCSF - Part 3 Introduction to Dependency Injection

Introduction

This article is actually part 3 of an introductory series of articles on Microsoft’s Composite Application Block (CAB) and Smart Client Software Factory (SCSF). However, this particular article will only talk about dependency injection in general terms and not discuss the CAB/SCSF. Part 5 of the series will address dependency injection in the CAB. Part 4 of the series expands the discussion here by talking about inversion of control (IoC) and dependency inversion.

这篇是这一系列的第三篇,但是这篇文章将会只谈论广义的依赖注入 不会涉及cab和scsf,part5会将依赖注入带入到cab里,part4会讨论反转控制和依赖反转。

 

Dependency Injection

Dependency injection as a general concept is explained far better than I could do it by Martin Fowler athttp://www.martinfowler.com/articles/injection.html. However, in short it is a means of allowing a main class to use a second, dependent class without directly referencing that class. Some external entity will provide the second class to the main class at runtime – it will ‘inject’ the ‘dependency’. Obviously to be useful the second class will need to implement an interface that the main class knows about.

Martin Fowler的文章比我更好的从广义上解释了依赖注入, 然而,简而言之,是允许一个主class可以在不直接引用第二个class的情况下调用它。一些外部的实体可以在运行时像主class提供第二个class。它将注入 这种依赖。 显然,对于第二个class,它需要一个接口,这样主class才可以使用它。

 

The aim of this is to let us change the behaviour of our class structure by changing which second class is injected into the main class. Because the main class has no hard dependency on the second class this can be done at runtime. Thus our code is very flexible, easy to modify and loosely coupled.

我们的目的是,让大家知道可以通过改变第二个class的注入来改变主class的表现。 因为主class并没有硬依赖第二个class,这些改变(注入)可以在运行时进行。 这样我们的代码就很具伸展性,松耦合,易于修改。

 

Class Diagram

The class diagram looks something like this:

Dependency Injection Class Diagram

MainClass contains a reference to an object that implements IDependentClass, and can thus call the DoSomethingInDependentClass method on that object. DependentClass1, DependentClass2, and DependentClass3 implement the interface.

主class耦合于接口,他有三个具体实现。 不解释了

 

Some client code will then create the appropriate dependent class and tell the main class to use it (via the interface). It’s easiest to understand this via some example code, which I’ll demonstrate below.

一些客户程序 将创建一个合适的独立的class 并且告诉主class去使用它(凭借接口),通过代码可以非常容易理解这些。。。

 

Strategy Pattern? (策略模式)

In many ways this is the classic Gang of Four Strategy pattern with a twist. The class diagram above is very similar to the usual one given for the strategy pattern: seehttp://www.dofactory.com/Patterns/PatternStrategy.aspx (don’t worry if you don’t know the strategy pattern at this stage though). The difference is that with dependency injection exactly which class is going to be provided as the dependency (strategy) is often left to configuration at runtime. That is, it can be abstracted out of the code entirely.

这就是经典的 Gang of Four   策略模式的的一个变形。上面的类图非常类似于Gang of Four策略模式(即使你不懂策略模式也不必担心) 通过依赖注入哪个具体的class会被作为依赖(策略)提供给主class 完全可以再运行时配置。 它可以完全被抽象出来。

 

In the Spring framework, for example, XML configuration files can be used to specify which class gets created and used. This is an extreme example of deciding which class to use at runtime: you can change the dependencies without changing the compiled code at all.

在spring framework里,举个例子, 通过配置xml 可以决定哪个类会被实例和使用 这个例子告诉我们 可以在不重新改变,编译代码的前提下改变注入。

 

However, there are some reasons for not using external configuration files to specify these dependencies: it makes it difficult to debug, and you’re probably not going to want to change application behaviour via a configuration file in any case. Dependency injection frameworks in general will allow you to specify your injection in code (Spring allows this).

然而,有些理由让我们并不要使用外部配置去定于具体的依赖, 因为那会使得调试变得困难, 而且你也并不想通过配置文件来改变一切,这一架构还是允许你通过编码改变依赖的。

 

Dependency Injection Example

We can easily create an example that does this using .NET. Full code for all these examples is available for download.

Firstly we create an interface that will be used to call our dependent class:

    public interface IDependentClass      {          void DoSomethingInDependentClass();      }   

Then we create dependent classes that implement this interface to do something. Which one of these classes will actually be used will only be decided at runtime based on a setting in the App.Config file:

public class DependentClass1 : IDependentClass
{
    public void DoSomethingInDependentClass()
    {
        Console.WriteLine("Hello from DependentClass1: I can be injected into MainClass");
    }
}         public class DependentClass2 : IDependentClass
{
    public void DoSomethingInDependentClass()
    {
        Console.WriteLine("Hello from DependentClass2: I can be injected as well, just change App.Config");
    }
}


Now we create a class that will use the dependent classes, calling their functionality via the interface. This ‘main’ class has no knowledge of the dependent classes types, only of the interface that they implement:

public class MainClass
{
    IDependentClass dependentClass;
    public IDependentClass DependentClass
    {
        get
        {
            return dependentClass;
        }
        set
        {
            dependentClass = value;
        }
    }
    public void DoSomething()
    {
        dependentClass.DoSomethingInDependentClass();
    }
}  

Now we need some code that will use this structure. Clearly we need to instantiate the main class and tell it (using the interface) which dependent class to use. We can then call the functionality on the main class.

 

Finally we need some code in GetCorrectDependency() that decides which dependent class we are going to use, and instantiates it. We do this by examining the ‘ClassName’ setting in App.Config, and instantiating that using Activator.CreateInstance:

static IDependentClass GetCorrectDependency()
{
    string classToCreate = System.Configuration.ConfigurationManager.AppSettings["ClassName"];
    Type type = System.Type.GetType(classToCreate);
    IDependentClass dependency = (IDependentClass)Activator.CreateInstance(type);
    return dependency;
}

The App.Config file is as below:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="ClassName" value="DependencyInjection.DependentClass1" />
  </appSettings>
</configuration>

So we can compile this code and run it as above and it will output the message in DependentClass1. If we then just change the configuration file so that the ClassName value is DependencyInjection.DependentClass2 and re-run the code will output the message in DependentClass2.

Note that what is powerful about this code is we could write a DependentClass3, with new behaviour, and use it by changing the configuration file without the need to change any of the existing code.

以上是具体例子,已经写得很清晰了,不解释了。

 

Types of Dependency Injection(依赖注入的类型)

Martin Fowler’s article talks about three different types of dependency injection: constructor injection, interface injection and setter injection. The example above uses setter injection: we give the main class its dependency via a setter in the line:

mainClass.DependentClass = dependency;

martin fowler的文章谈及了三种不同类型的依赖注入, 构造器注入, 接口注入, setter注入。 上面的例子是 通过setter 来实现注入的,

 

In constructor injection we give the main class its dependency in the constructor. With interface injection we define another interface that the main class will implement that sets the dependency.

构造器注入 是通过构造函数来传递注入的, 接口注入是让主class再继承一个接口,并注入依赖。

I have reworked the example above for both constructor injection and interface injection. As you can see these are relatively trivial changes in this simple example.

我重写了上面的代码 使其成为构造器注入,和接口注入,你可以看到我只是做了一些相关的微小改动。

Full C# code for these examples is available.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值