依赖注入(DI)是控制反转(IoC)的一种方式。目前在.NET和Java领域已经有相当多基于DI思想的对象容器如SpringUnity等。本文试图避免重复性地介绍DI基础知识和DI容器的使用而是希望深一层探讨DI的本质和对象间关系以达到合理设计避免滥用DI的目的。依赖注入 vs 创建对象有不少地方这样描述“依赖注入改变了使用对象前先创建的传统方式而是从外部注入依赖的对象”。这样的描述其实似是而非先来看一个例子interface ICar{void Run();}class Person{public ICar Car {set { m_car value; }}public void Drive() { m_car.Run(); }private ICar m_car;}Person不主动创建所依赖的ICar对象而是通过DI方式注入。应该说这是一种合理的设计但如果接本段开头的话说“依赖注入改变了人使用汽车之前先创建的传统方式”您会不会觉得别捏呢我们来看看所谓的“传统方式”是什么样子class Benz : ICar{void Run() {}}class Person{public void Drive() {ICar car new Benz();m_car.Run();}}这就是Drive之前先创建ICar对象“传统方式”并不传统因为它非常不自然。我们肯定在偷笑“这个人家里一定是开银行的要开车的时候临时买一辆奔驰开完马上扔给垃圾回收站”。这说明DI并非都是反传统的求新求变在某些情况下它本身就是一种合理的设计。不过这里话才说了一半上面的例子是说“不该创建对象的时候DI本来就是一种合理的设计”下面还有另一半“该创建对象的时候用DI反而不合理”。class Person{public Person(IHeart heart) { m_heart heart; }private IHeart m_heart;}大家看看上面这位老兄在干嘛呢心脏也依赖注入这就是典型的滥用依赖注入。我们来分析一下这种方式的问题在哪儿1. 暴露内部实现假设m_heart只是内部实现相关的对象上面的方式就暴露了内部实现造成外部程序对Person的内部实现的变化变得敏感2. 依赖对象状态被外部修改由于m_heart是从外部注入的外部可能依然持有m_heart的引用因此完全可能被有意无意地修改掉。本质上这种滥用DI的问题其实是对OO封装的破坏。如果采用创建对象的方式这个例子就合理多了class Heart : IHeart { ... }class Person{public Person() { m_heart new Heart(); }private IHeart m_heart;}Person在其构造函数内自行创建m_heart显然是合理的设计不存在上面强行引入DI造成的破坏封装问题。对象间依赖关系上面的两个例子从不同方面说明了DI和对象创建谁也不能取代谁应该根据情况采用合理的设计。那么我们自然要问合理的标准在哪里呢有没有明确的指导方针供我们设计时参考呢答案是有那就是对象间关系。在OO中对象间关系的大致可分为两类纵向关系和横向关系。纵向主要指继承关系比较容易区分但横向关系比较微妙。从本文的例子中我们已经明显感觉到了横向关系是需要仔细区分的。按UML建议横向关系大体分为4种它们的耦合程度由弱到强依赖 关联 聚合 组合1. 依赖Dependency语义“a uses b”a依赖于b但不持有b的引用比如现实世界的例子有“人对空气的依赖关系”在程序中a.f(b)可以理解为a对b的依赖或者说对b的类型B的依赖b作为a的方法参数a内部成员变量不引用b。UML符号编辑2. 关联Association语义“a has b”a拥有b的引用但a和b无从属关系二者是一种松散的关联关系可以随时解除或建立比如本文中“人与汽车关系”。UML符号编辑3. 聚合Aggregation语义“a owns b”a拥有b的引用且有从属关系二者的耦合比关联更强但a并不负责b的生命周期。在程序中b并非a专有的内部实现细节除a以为外部也可能引用b。比如”汽车和轮胎关系”汽车不负责轮胎的生产汽车报废了轮胎或许还可以继续使用。UML符号编辑4. 组合Composition语义“b is a part of a”a不仅拥有b的引用还应该全权负责b的整个生命周期在程序中b通常是a的内部实现细节不暴露给外部比如本文中人与心脏的关系。UML符号编辑我们注意这四种关系中有一个关键的概念“对象生命周期”在建模时辨别清楚对象生命周期就不难选择采用DI还是创建对象。DI意味着使用者不负责依赖对象的生命周期创建对象则相反。对应到上面的4种横向关系我们一般可以这样处理第1种依赖关系不需要DI也不需要创建对象第23种关联和聚合关系适合采用DI方式最后一种组合关系适合采用创建对象方式。
依赖注入与对象间关系
发布时间:2026/7/6 3:59:57
依赖注入(DI)是控制反转(IoC)的一种方式。目前在.NET和Java领域已经有相当多基于DI思想的对象容器如SpringUnity等。本文试图避免重复性地介绍DI基础知识和DI容器的使用而是希望深一层探讨DI的本质和对象间关系以达到合理设计避免滥用DI的目的。依赖注入 vs 创建对象有不少地方这样描述“依赖注入改变了使用对象前先创建的传统方式而是从外部注入依赖的对象”。这样的描述其实似是而非先来看一个例子interface ICar{void Run();}class Person{public ICar Car {set { m_car value; }}public void Drive() { m_car.Run(); }private ICar m_car;}Person不主动创建所依赖的ICar对象而是通过DI方式注入。应该说这是一种合理的设计但如果接本段开头的话说“依赖注入改变了人使用汽车之前先创建的传统方式”您会不会觉得别捏呢我们来看看所谓的“传统方式”是什么样子class Benz : ICar{void Run() {}}class Person{public void Drive() {ICar car new Benz();m_car.Run();}}这就是Drive之前先创建ICar对象“传统方式”并不传统因为它非常不自然。我们肯定在偷笑“这个人家里一定是开银行的要开车的时候临时买一辆奔驰开完马上扔给垃圾回收站”。这说明DI并非都是反传统的求新求变在某些情况下它本身就是一种合理的设计。不过这里话才说了一半上面的例子是说“不该创建对象的时候DI本来就是一种合理的设计”下面还有另一半“该创建对象的时候用DI反而不合理”。class Person{public Person(IHeart heart) { m_heart heart; }private IHeart m_heart;}大家看看上面这位老兄在干嘛呢心脏也依赖注入这就是典型的滥用依赖注入。我们来分析一下这种方式的问题在哪儿1. 暴露内部实现假设m_heart只是内部实现相关的对象上面的方式就暴露了内部实现造成外部程序对Person的内部实现的变化变得敏感2. 依赖对象状态被外部修改由于m_heart是从外部注入的外部可能依然持有m_heart的引用因此完全可能被有意无意地修改掉。本质上这种滥用DI的问题其实是对OO封装的破坏。如果采用创建对象的方式这个例子就合理多了class Heart : IHeart { ... }class Person{public Person() { m_heart new Heart(); }private IHeart m_heart;}Person在其构造函数内自行创建m_heart显然是合理的设计不存在上面强行引入DI造成的破坏封装问题。对象间依赖关系上面的两个例子从不同方面说明了DI和对象创建谁也不能取代谁应该根据情况采用合理的设计。那么我们自然要问合理的标准在哪里呢有没有明确的指导方针供我们设计时参考呢答案是有那就是对象间关系。在OO中对象间关系的大致可分为两类纵向关系和横向关系。纵向主要指继承关系比较容易区分但横向关系比较微妙。从本文的例子中我们已经明显感觉到了横向关系是需要仔细区分的。按UML建议横向关系大体分为4种它们的耦合程度由弱到强依赖 关联 聚合 组合1. 依赖Dependency语义“a uses b”a依赖于b但不持有b的引用比如现实世界的例子有“人对空气的依赖关系”在程序中a.f(b)可以理解为a对b的依赖或者说对b的类型B的依赖b作为a的方法参数a内部成员变量不引用b。UML符号编辑2. 关联Association语义“a has b”a拥有b的引用但a和b无从属关系二者是一种松散的关联关系可以随时解除或建立比如本文中“人与汽车关系”。UML符号编辑3. 聚合Aggregation语义“a owns b”a拥有b的引用且有从属关系二者的耦合比关联更强但a并不负责b的生命周期。在程序中b并非a专有的内部实现细节除a以为外部也可能引用b。比如”汽车和轮胎关系”汽车不负责轮胎的生产汽车报废了轮胎或许还可以继续使用。UML符号编辑4. 组合Composition语义“b is a part of a”a不仅拥有b的引用还应该全权负责b的整个生命周期在程序中b通常是a的内部实现细节不暴露给外部比如本文中人与心脏的关系。UML符号编辑我们注意这四种关系中有一个关键的概念“对象生命周期”在建模时辨别清楚对象生命周期就不难选择采用DI还是创建对象。DI意味着使用者不负责依赖对象的生命周期创建对象则相反。对应到上面的4种横向关系我们一般可以这样处理第1种依赖关系不需要DI也不需要创建对象第23种关联和聚合关系适合采用DI方式最后一种组合关系适合采用创建对象方式。