首先通过下面简单的例子理解依赖注入思想。假如你正在玩一个游戏,勇士们为荣耀而战。首先需要为我们的勇士装备合适的武器。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 刀
10 /// </summary>
11 public class Sword
12 {
13 public void Hit(string target)
14 {
15 Console.WriteLine("Hit the target {0} by sword", target);
16 }
17 }
18 }
接着,创建类Samurai来表示勇士。为攻击敌人,我们创建一个攻击方法Attack。当这个方法被调用时,勇士将用刀攻击对手。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 勇士
10 /// </summary>
11 public class Samurai
12 {
13 private Sword _sword;
14
15 public Samurai()
16 {
17 _sword = new Sword();
18 }
19
20 public void Attack(string target)
21 {
22 _sword.Hit(target);
23 }
24 }
25 }
创建勇士参加战斗。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 class Program
9 {
10 static void Main(string[] args)
11 {
12 Samurai s = new Samurai();
13 s.Attack("enemy");
14
15 Console.ReadKey();
16 }
17 }
18 }
如果想为勇士装备其它的武器,该如何做呢?因为Sword 对象在Samurai的构造函数中被创建,所以我们不得不修改类的实现。
当一个类依赖一个实例,称为紧耦合(tightly coupled)。在上面例子中,Samurai类相对于Sword类是紧耦合的。当类是紧耦合,在没有修改实现的情况下,被依赖项是不能被交换的。为了避免紧耦合,我们可以使用接口提供间接层。
创建武器接口。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 武器
10 /// </summary>
11 public interface IWeapon
12 {
13 void Hit(string target);
14 }
15 }
接着,修改类Sword实现接口IWeapon。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 刀
10 /// </summary>
11 public class Sword : IWeapon
12 {
13 public void Hit(string target)
14 {
15 Console.WriteLine("Hit the target {0} by sword", target);
16 }
17 }
18 }
修改Samurai类。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 勇士
10 /// </summary>
11 public class Samurai
12 {
13 private IWeapon _weapon;
14
15 public Samurai()
16 {
17 _weapon = new Sword();
18 }
19
20 public void Attack(string target)
21 {
22 _weapon.Hit(target);
23 }
24 }
25 }
这样勇士可以使用不同的武器,但是Sword还是在Samurai的构造函数中被创建,Samurai 类相对于Sword类还是紧耦合的。为了让勇士使用其它的兵器,我们还要修改类Samurai的实现。
将Sword作为参数传递给Samurai的构造函数。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 勇士
10 /// </summary>
11 public class Samurai
12 {
13 private IWeapon _weapon;
14
15 public Samurai(IWeapon weapon)
16 {
17 _weapon = weapon;
18 }
19
20 public void Attack(string target)
21 {
22 _weapon.Hit(target);
23 }
24 }
25 }
这样,我们可以通过Samurai的构造函数注入Sword,这就是依赖注入。
接着,为勇士创建新的武器镖。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 /// <summary>
9 /// 镖
10 /// </summary>
11 public class Shuriken : IWeapon
12 {
13 public void Hit(string target)
14 {
15 Console.WriteLine("Hit the target {0} by shuriken", target);
16 }
17 }
18 }
接着,创建军队。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Inject
7 {
8 class Program
9 {
10 static void Main(string[] args)
11 {
12 // 使用刀
13 Samurai s1 = new Samurai(new Sword());
14 s1.Attack("enemy");
15
16 // 使用镖
17 Samurai s2 = new Samurai(new Shuriken());
18 s2.Attack("enemy");
19
20 Console.ReadKey();
21 }
22 }
23 }
这就是手动依赖注入。每次创建一个Samurai,需要先创建实现了IWeapon接口的对象,传递给Samurai的构造函数。现在我们改变勇士使用的武器,就不用再修改Samurai类。Samurai被分离出来,我们可以创建新的武器而不关心Samurai的代码。
手动依赖注入对于小项目是有效的策略,但是随着应用程序的大小和复杂性的增长,关联对象将变得越来越繁杂。接下来我们将使用Ninject实现依赖注入。
参考资料: