IoC

最近IoC很热门,一般翻译为控制反转,也就是一个类将一些工作交给framework(或者控制器)来做。从本质上讲是面向对象的设计原则之一——接口可具体实现的分离的一个具体体现,对象之间解耦的一种方法。

比如说,我们希望设计一个更好用的DropDownList控件,它可以按照如下的方式工作

在这里,我们只关注DataSourceKey这个属性,DataSourceKey是指控件希望根据这个key就可以得到data source,然后自动绑定,那么怎么根据key来得到data source呢,我们需要一个services来完成这件工作

于是我们定义一个接口 IDataSourceProvider

interface IDataSourceProvider
{ ICollection GetDataSource(string key); }

然后有一个类来实现这个接口
class MyDataSourceProvider : IDataSourceProvider
{
ICollection GetDataSource(string key)
{ ... }
}

class ExDropDownList
{
IDataSourceProvider myProvider;

......

if(AutoBind==true &&
IsDataBound
==false)
{
IDataSourceProvider myProvider= (IDataSourceProvider) new MyDataSourceProvider ();
DataSource = myProvider.GetDataSource(DataSourceKey);
DataBind();
}

..............
}


这不是一种好的做法,因为这种实现方法大大加强了ExDropDownList和DataSourceProvider之间的依赖关系,在这个例子里,ExDropDownList不仅依赖接口IDataSourceProvider,而且依赖接口的具体实现MyDataSourceProvider,不符合面向对象的原则,如果只是自己项目用问题还不是很大,但是如果别的team也希望用这个控件,但是他们有另外一种提供data source的方法,那么应该怎么办呢。

解耦的办法由很多种,其中一种是把具体创建IDataSourceProvider实例的工作交给容器或者framework来做,也就是说将对IDataSourceProvider的控制反转,有容器决定创建哪一个IDataSourceProvider实例,

接下来,怎么把创建好的IDataSourceProvider实例告诉ExDropDownList呢(或者说注入,Injection)
有三种方法,一种是构造注入,也就是在创建ExDropDownList时就告诉它,哪一个IDataSourceProvider实例可以为你提供GetDataSource的服务

class ExDropDownList
{
private IDataSourceProvider provider;

ExDropDownList(IDataSourceProvider provider)
{
this.Provider = provider;
}
}


另一种方法是设值注入,也就是ExDropDownList对外公布一个方法setProvider或者公布一个属性Provider,让容器可以把创建好的IDataSourceProvider实例传给ExDropDownList。

class ExDropDownList
{
private IDataSourceProvider provider;

public Provider
{
get{}
set{}
}
}

....
ExDropDownList.Provider = (IDataSourceProvider) new MyDataSourceProvider ()
....

第三种是接口注入,也就是说调用者和服务提供者定义一个协议,ExDropDownList如果需要使用DataSourceProvider提供的服务,就需要根据协议实现一个InjectProvider的接口,以便于容器通过这个接口将IDataSourceProvider实例传给ExDropDownList

interface IInjectProvider
{ viod SetProvider(IDataSourceProvider provider ); }

class ExDropDownList : IInjectProvider
{
SetProvider(IDataSourceProvider provider)
{
this.provider = provider;
}
}


((IInjectProvider)ExDropDownList).SetProvider(IDataSourceProvider) new MyDataSourceProvider ());

接口注入的方法因为对象间还是有很高的耦合度,所以应用不是很广泛。

构造注入和设值注入那一种更好呢。
martin fowler给出了一个解释:
构造注入和设值注入反映了面向对象编程的一个普遍问题,应该在哪里填充对象的字段,是在创建对象时还是通过属性或者方法来设定值。martin fowler建议在对象构造时就创建完正合法的对象,这样做还有一个好处就是可以隐藏不可改变的字段(注:或者不应该在运行期间随意
改变的字段),而公布一个设置方法,就意味着调用者可以自由改变字段的值。

凡事总有例外,如果参数太多,构造注入使系统显得有点凌乱,而且有些时候没有办法使用构造注入,比如上面ExDropDownList 的例子,创建ExDropDownList 实例不是我们的framework能够控制的。需要设值注入来协助。

我们怎么做的呢?把ExDropDownList的provider定义为静态属性,然后在Application_BeginRequest事件赋值
DataSourceProvider provider = new DataSourceProvider();
ExDropDownList.dataSourceProvider = provider;

这属于代码配置

用ExDropDownList 的例子来说明IoC不太恰当,因为ExDropDownList不是我们的framework能够完全控制的。除了IoC这种解耦方法外还有别的方法比如在config文件定义哪一个Assembly可以提供GetDataSource服务,或者我们定义一个ServicesFind类,它知道怎么样可以得到我们所需的IDataSourceProvider实例。

IoC现在看来,主要是用来解决接口参数的注入。framework可以根据很简单的约定,将不同组件组装起来,IoC不是新的事物,也不是哪一种语言的特有功能,只是一种组件的协作和组织方式。


05-29
### 什么是 Inversion of Control (IoC) 在软件开发中的意义 Inversion of Control (IoC) 是一种设计原则,用于反转对象创建和依赖管理的控制权。传统的应用程序中,程序逻辑通常由开发者直接控制,例如通过硬编码或顺序调用来决定对象的创建和使用方式[^1]。然而,在 IoC 的模式下,这种控制被反转,将对象的创建和管理交给外部容器或框架来完成。 在用户界面开发中,IoC 的概念也可以体现在事件驱动模型中。早期的命令式用户界面由应用程序直接控制流程,例如通过一系列指令提示用户输入数据。而在现代图形化用户界面中,UI 框架接管了主循环的控制权,应用程序只需提供事件处理器[^2]。这种方式使得应用程序逻辑与 UI 控制分离,增强了灵活性和可维护性。 此外,IoC 并不是某个特定技术的独有特性,而是许多框架的核心设计理念之一。例如,轻量级容器(如 Spring)通过实现 IoC 来简化依赖注入和对象管理。虽然有人认为 IoC 是框架的基本特征,类似于汽车需要轮子一样自然[^3],但其核心价值在于如何通过反转控制来提升代码的解耦性和复用性。 为了更好地理解 IoC 的实际应用,可以考虑一个汽车制造的例子。如果按照传统的方式设计汽车,每个组件的构造函数都可能直接依赖于底层组件的实现细节。例如,Car 类可能需要关心 Framework 类的构造过程,而 Framework 又可能依赖 Bottom 和 Tire 等更底层的组件[^4]。这种紧耦合的设计会导致修改任何一个底层组件时都需要调整大量的上层代码。 相比之下,通过应用 IoC 原则,可以将组件的创建和依赖管理交给外部容器来处理。例如,使用依赖注入的方式,让 Car 对象不再直接负责创建 Framework 实例,而是从容器中获取已经准备好的实例。这样即使底层组件发生变化,也不会直接影响到上层代码,从而提高了系统的灵活性和可维护性。 ```python # 传统方式:Car 直接负责创建 Framework class Tire: def __init__(self, size=30): self.size = size class Bottom: def __init__(self, tire: Tire): self.tire = tire class Framework: def __init__(self, bottom: Bottom): self.bottom = bottom class Car: def __init__(self): tire = Tire() bottom = Bottom(tire) self.framework = Framework(bottom) # 使用 IoC 的方式:Car 不再负责创建 Framework class Car: def __init__(self, framework: Framework): self.framework = framework # 外部容器负责创建和注入依赖 tire = Tire(size=35) # 动态设置轮胎尺寸 bottom = Bottom(tire) framework = Framework(bottom) car = Car(framework) ``` ### 结论 IoC 是一种重要的设计原则,能够显著改善软件系统的结构和可维护性。通过将对象的创建和依赖管理交给外部容器或框架,开发者可以减少代码间的耦合度,使系统更加灵活和易于扩展。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值