控制反转(Inversion of Control IoC)在java中,Spring就是一个很好的应用。用于解除使用者和生产者的耦合。
一般的代码中。使用者即是生产者,使用者在调用它需要的对象的时候,去创建它(new a instance),然后使用。这样就造成了强依赖。
简单说来就是:a调用b,那么a 就依赖于b. 而控制反转呢,就是用来解除这样一来的一种方式。
演变成: a调用 构造容器c ,构造容器c去创建b. 这样的好处是, b是可变的,解除了a和b的依赖,将依赖延迟到 构造容器c 中了。但依赖依旧存在。
这样就产生了一个控制的反转效果,a不再控制b的形态,而反倒由b的“构造容器c"来控制。
DI(Dependency Injection) 和 服务定位器(Service Location) 是IoC模式的两个实现方法(模式)。
我们想做一个演示,来一步步的演示这个过程。 例如:我们想模拟士兵持武器进行攻击的场景。
一般我们会写出这样的代码,构造两个类,一个士兵,一个武器。还有个就是main方法。代码如下
/* demo1 author:vir56k@163.com date:2009-8-12 */ //武器 class Weapon { public void Attack() { Console.WriteLine("用武器攻击"); } }
//士兵 class Soldier { /* p1 */ private Weapon _Weapon = new Weapon();//赋予武器;
public Weapon Weapon { get { return _Weapon; } set { _Weapon = value; } }
public void Attack() { Weapon.Attack(); } }
class Program { static void Main(string[] args) { Soldier soldier = new Soldier();//构建一个战士 soldier.Attack();//攻击
Console.Read(); } } 我们看到,士兵和武器是耦合在一起的,这是一个强依赖。
场景变化要求:我们觉得武器太广泛了,武器还是有种类的,有剑和匕首。于是场景更改如下:
例如:我们想模拟士兵持武器进行攻击的场景,士兵可以使用的武器有剑和匕首。
于是我们更改了我们的代码,为武器抽象一个接口,它有两个实现类,一个是剑,一个是匕首类。代码如下:
interface IWeapon { void Attack(); }
class Sword : IWeapon { public void Attack() { Console.WriteLine("用剑攻击"); } }
class Knife : IWeapon { public void Attack() { Console.WriteLine("用匕首攻击"); } }
//士兵 class Soldier { /* p2 */ private IWeapon _Weapon = new Sword(); /* p3 */// private IWeapon _Weapon = new Knife();
public IWeapon Weapon { get { return _Weapon; } set { _Weapon = value; } }
public void Attack() { Weapon.Attack(); } } class Program { static void Main(string[] args) { Soldier soldier = new Soldier(); soldier.Attack();
Console.Read(); } }
我们看到,这下我们可以选择武器了,代码位置p2和p3可以让我们选择使用哪个武器。我们在需要更换武器的时候,就注释掉 第p2 或者第p3行,然后重新 编译我们的代码。但每次的更改都需要重新编译,确实让人觉得很讨厌。于是我们想到解除耦合。 写一个 构造容器c,来解除a对b的依赖。
代码如下: interface IWeapon { void Attack(); }
class Sword : IWeapon { public void Attack() { Console.WriteLine("用剑攻击"); } }
class Knife : IWeapon { public void Attack() { Console.WriteLine("用匕首攻击"); } }
//士兵 class Soldier { private IWeapon _Weapon;
public IWeapon Weapon { get { return _Weapon; } set { _Weapon = value; } }
public void Attack() { Weapon.Attack(); } }
//应用程序上下文 public class ApplicationContext { public object GetObject(string id) { if (id == "Soldier") { Soldier solider = new Soldier(); solider.Weapon = new Sword(); return solider; } return null; } }
class Program { static void Main(string[] args) { ApplicationContext ctx = new ApplicationContext(); Soldier soldier = (Soldier)ctx.GetObject("Soldier");//返回配置好的士兵 soldier.Attack();
Console.Read(); } }
我们看到,战士类不再依赖具体的武器,而改为依赖 武器接口。剑类和匕首类依赖 武器接口。 Program中,调用 应用程序上下文类 获得组装好的(配置好 的)战士,然后执行攻击方法。 应用程序上下文类,实现了组装各种元素的功能,专业说来:就是 装配程序组件。
它实现了 组件的配置和使用的分离。
但我们看到,我们如果想要更改成其他武器,就仍然得更改代码重新编译。那么配置文件或许是个好的解决方案。 最终代码如下: interface IWeapon { void Attack(); }
class Sword : IWeapon { public void Attack() { Console.WriteLine("用剑攻击"); } }
class Knife : IWeapon { public void Attack() { Console.WriteLine("用匕首攻击"); } }
//士兵 class Soldier { private IWeapon _Weapon;
public IWeapon Weapon { get { return _Weapon; } set { _Weapon = value; } }
public void Attack() { Weapon.Attack(); } }
//应用程序上下文 public class ApplicationContext { public object GetObject(string id) { //解析xml配置文件,并反射创建对象 XmlDocument doc = new XmlDocument(); doc.Load("DI_Config.xml"); XmlNode node = doc.SelectSingleNode(string.Format("//beans/bean[@id='{0}']",id)); if (node != null) { string className = node.Attributes["class"].Value; Type tp = Assembly.GetExecutingAssembly().GetType(className); object objVal = Activator.CreateInstance(tp); XmlNodeList nodeProperties = node.SelectNodes("./property"); foreach (XmlNode var in nodeProperties) { string propertyName = var.Attributes["name"].Value; foreach (PropertyInfo pInfo in tp.GetProperties()) { if (pInfo.Name == propertyName) { string clsRef = var.SelectSingleNode("./ref").Attributes["local"].Value; XmlNode node3 = doc.SelectSingleNode(string.Format("//beans/bean[@id='{0}']",clsRef)); if (node3 != null) { string className2 = node3.Attributes["class"].Value; Type tp2 = Assembly.GetExecutingAssembly().GetType(className2); pInfo.SetValue(objVal,Activator.CreateInstance(tp2),null); } } } } return objVal; }
return null; } }
class Program { static void Main(string[] args) { ApplicationContext ctx = new ApplicationContext(); Soldier soldier = (Soldier)ctx.GetObject("Soldier");//返回配置好的士兵 soldier.Attack(); Console.Read(); } }
在 应用程序上下文中,通过反射,获得配置文件里的设置。为 战士对象 配置 指定的武器。这就是一个注入的过程。我们这里采用的方法是 设置方法注入,另外的两种注入方法是,构造方法注入和 接口注入。
更重要的是:配置和使用分开。
下载代码资料参考: martin fowler 的Ioc容器和Dependency Injection模式。 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|