在浏览有关(服务定位器)设计模式的文章时,我一直在研究构造函数注入和依赖注入这两个术语。
当我搜索构造函数注入时,我得到的结果不清楚,这促使我在这里进行检查。
什么是构造函数注入?这是一种特定类型的依赖项注入吗?一个典型的例子将有很大的帮助!
编辑
一周的时间后,我又重新审视了这个问题,我看到自己迷失了……万一其他人突然出现在这里,我将通过一点我的学习来更新问题的正文。请随时发表评论/纠正。 构造函数注入和属性注入是两种类型的依赖注入。
在浏览有关(服务定位器)设计模式的文章时,我一直在研究构造函数注入和依赖注入这两个术语。
当我搜索构造函数注入时,我得到的结果不清楚,这促使我在这里进行检查。
什么是构造函数注入?这是一种特定类型的依赖项注入吗?一个典型的例子将有很大的帮助!
编辑
一周的时间后,我又重新审视了这个问题,我看到自己迷失了……万一其他人突然出现在这里,我将通过一点我的学习来更新问题的正文。请随时发表评论/纠正。 构造函数注入和属性注入是两种类型的依赖注入。
Answers:
我不是专家,但我想可以提供帮助。是的,这是一种特定类型的依赖注入。
免责声明:Ninject Wiki几乎所有这些都是“被盗” 的
让我们通过简单的示例来研究依赖注入的概念。假设您正在编写下一个轰动一时的游戏,其中高贵的战士为荣耀而战。首先,我们需要一种适合武装战士的武器。
class Sword
{
public void Hit(string target)
{
Console.WriteLine("Chopped {0} clean in half", target);
}
}
然后,让我们创建一个代表战士自己的类。为了攻击敌人,战士需要一个Attack()方法。调用此方法时,应使用其剑来打击其对手。
class Samurai
{
readonly Sword sword;
public Samurai()
{
this.sword = new Sword();
}
public void Attack(string target)
{
this.sword.Hit(target);
}
}
现在,我们可以创建我们的武士并进行战斗!
class Program
{
public static void Main()
{
var warrior = new Samurai();
warrior.Attack("the evildoers");
}
}
就像您可能想象的那样,这将把切碎的邪恶者打印到控制台一半。这很好用,但是如果我们想用另一把武器武装武士怎么办?由于Sword是在Samurai类的构造函数中创建的,因此我们必须修改类的实现才能进行此更改。
当一个类依赖于具体的依赖时,据说它与该类紧密相关。在此示例中,Samurai类与Sword类紧密耦合。当类紧密耦合时,在不更改其实现的情况下不能互换它们。为了避免紧密耦合类,我们可以使用接口来提供一个间接级别。让我们创建一个界面来表示我们游戏中的武器。
interface IWeapon
{
void Hit(string target);
}
然后,我们的Sword类可以实现此接口:
class Sword : IWeapon
{
public void Hit(string target)
{
Console.WriteLine("Chopped {0} clean in half", target);
}
}
我们可以更改武士职业:
class Samurai
{
readonly IWeapon weapon;
public Samurai()
{
this.weapon = new Sword();
}
public void Attack(string target)
{
this.weapon.Hit(target);
}
}
现在我们的武士可以装备不同的武器。可是等等!剑仍在武士的构造函数中创建。由于我们仍然需要更改武士的实现方式来为我们的战士提供另一种武器,因此武士仍然与剑紧密相连。
幸运的是,有一个简单的解决方案。与其从Samurai的构造函数中创建Sword,不如将其公开为构造函数的参数。也称为构造函数注入。
class Samurai
{
readonly IWeapon weapon;
public Samurai(IWeapon weapon)
{
this.weapon = weapon;
}
public void Attack(string target)
{
this.weapon.Hit(target);
}
}
正如Giorgio指出的那样,还有财产注入。就像这样:
class Samurai
{
IWeapon weapon;
public Samurai() { }
public void SetWeapon(IWeapon weapon)
{
this.weapon = weapon;
}
public void Attack(string target)
{
this.weapon.Hit(target);
}
}
希望这可以帮助。
我将尽力使它变得非常基础。这样,您可以在概念的基础上创建自己的复杂解决方案或想法。
现在想象我们有一个名为禧年的教堂,该教堂在世界各地都有分支机构。我们的目标是简单地从每个分支中获取一个项目。这就是使用DI的解决方案;
1)创建一个接口IJubilee:
public interface IJubilee
{
string GetItem(string userInput);
}
2)创建一个类JubileeDI,它将把IJubilee接口作为构造函数并返回一个项目:
public class JubileeDI
{
readonly IJubilee jubilee;
public JubileeDI(IJubilee jubilee)
{
this.jubilee = jubilee;
}
public string GetItem(string userInput)
{
return this.jubilee.GetItem(userInput);
}
}
3)现在创建Jubilee的三个分支,即JubileeGT,JubileeHOG,JubileeCOV,它们都必须继承IJubilee接口。有趣的是,让其中一个将其方法实现为虚拟方法:
public class JubileeGT : IJubilee
{
public virtual string GetItem(string userInput)
{
return string.Format("For JubileeGT, you entered {0}", userInput);
}
}
public class JubileeHOG : IJubilee
{
public string GetItem(string userInput)
{
return string.Format("For JubileeHOG, you entered {0}", userInput);
}
}
public class JubileeCOV : IJubilee
{
public string GetItem(string userInput)
{
return string.Format("For JubileCOV, you entered {0}", userInput);
}
}
public class JubileeGTBranchA : JubileeGT
{
public override string GetItem(string userInput)
{
return string.Format("For JubileeGT branch A, you entered {0}", userInput);
}
}
4)就是这样!现在,让我们看看DI的作用:
JubileeDI jCOV = new JubileeDI(new JubileeCOV());
JubileeDI jHOG = new JubileeDI(new JubileeHOG());
JubileeDI jGT = new JubileeDI(new JubileeGT());
JubileeDI jGTA = new JubileeDI(new JubileeGTBranchA());
var item = jCOV.GetItem("Give me some money!");
var item2 = jHOG.GetItem("Give me a check!");
var item3 = jGT.GetItem("I need to be fed!!!");
var item4 = jGTA.GetItem("Thank you!");
对于容器类的每个实例,我们传递所需的类的新实例,从而实现松散耦合。
希望这可以概括地解释这个概念。
假设您有一个类,A
每个类的实例都需要另一个类的实例B
。
class A
{
B b;
}
依赖注入意味着对引用的设置B
是由管理对象实例的对象设置的A
(与让类直接A
管理对引用的引用相反B
)。
构造函数注入意味着对的引用B
作为参数传递给的构造函数A
并在构造函数中设置:
class A
{
B b;
A(B b)
{
this.b = b;
}
}
一种替代方法是使用setter方法(或属性)将引用设置为B
。在这种情况下,管理一个实例的对象a
的A
必须先调用的构造A
和以后调用设置器方法来设置成员变量A.b
之前a
被使用。后者注射方法是在必要时对象包含对于彼此,例如循环引用如果一个实例b
的B
,在一个实例设置a
的A
包含一个回参考a
。
**编辑**
这里有一些更多的细节来解决这个评论。
1. A类管理B实例
class A
{
B b;
A()
{
b = new B();
}
}
2.在构造函数中设置了B实例
class A
{
B b;
A(B b)
{
this.b = b;
}
}
class M
{
...
B b = new B();
A a = new A(b);
}
3.使用setter方法设置B实例
class A
{
B b;
A()
{
}
void setB(B b)
{
this.b = b;
}
}
class M
{
...
B b = new B();
A a = new A();
a.setB(b);
}
itemA
或objectA
仅仅是增加名称)并不总是更好。