依赖注入 - 当你有很多依赖项时该怎么办?

Raf*_*cci 13 c# refactoring design-patterns dependency-injection

我有一个A级,取决于其他10个类.根据依赖注入模式,我应该通过其构造函数传递A的所有依赖项.

所以我们假设这个构造函数(当然这不是一个工作或真实的代码,因为我不允许在这里发布真正的代码)

public ClassA(ClassB b, ClassC c, ClassD d, ClassE e, ClassF f, ClassG g, ClassH h, ClassI i) {
  this.b = b;
  this.c = c;
  this.d = d;
  this.e = e;
  this.f = f;
  this.g = g;
  this.h = h;
  this.i = i;
}
Run Code Online (Sandbox Code Playgroud)

我已经阅读了Martin Fowler关于重构的书,即有一个带有大量参数的方法是一种代码气味,不应该发生.

我的问题是:当我们谈论DI时,这样可以吗?是否有更好的方法可以在不违反Martin Fowler规则的情况下注入依赖关系?

我知道我可以通过属性传递依赖项,但这可能会导致错误,因为没有人真正确定应该传递什么才能使类工作.

编辑

谢谢你的所有答案.我现在将尝试演示一些A类依赖项:

1 - 访问数据库
2的类 - 访问另一个数据库的另一个类(是的,我需要在两个数据库上执行操作)
3 - 通过电子邮件发送错误通知的
类4 - 加载配置
的类5 - 一个类将作为某些操作的计时器(也许这可以避免)
6 - 具有业务逻辑的类

我试图摆脱任何其他许多其他人,但这些是非常必要的,我没有看到任何避免它们的方法.

编辑

经过一些重构后,我有7个依赖项(从10开始).但我有4个DAO对象:

CustomerDAO
ProcessDAO
ProductsDAO
CatalogDAO

是否正确创建另一个名为MyProjectDAO的类并将其注入其中?这样我只有一个DAO类聚合我项目的所有DAO对象.我不认为这是一个好主意,因为它违反了单一责任原则.我对吗?

Jon*_*eet 14

在我的经验中:

  • 尝试设计您的类,以便它需要更少的依赖项.如果它需要那么多,它可能有太多的责任.
  • 如果您确信您的类设计是合适的,请考虑将某些依赖项连接在一起是否有意义(例如,通过适配器负责一个类需要的一个"大"操作,委托给几个的依赖关系).然后,您可以依赖适配器而不是"较小"的依赖项.
  • 如果其他每一点真的有意义,只要吞下有很多参数的气味.它有时会发生.

  • @SebastianWeber:绝对.专业代码*总是比设计书中出现的精心设计的情况更丑陋:)在某些情况下它很难看但仍然合适; 在其他情况下,这是可以避免的,绝对*应该避免; 在其他情况下,它可以通过很多努力来避免,但努力并不值得. (8认同)

cco*_*ley 11

你能证明(为了你自己)为什么班级依赖于其他10个班级吗?是否有成员变量用于将这些类的子集绑定在一起?如果是这样,那表明该类应该被拆分,以便提取的类将依赖于子集,并且将这种状态联系在一起的变量在提取的类中.有10个依赖项,这个类可能只是变得太大了,无论如何都需要将其内部分解.

关于你的最后一句话的注释:这样的顺序依赖也可能是代码气味,所以最好不要在你的界面中公开它.实际上,考虑订单要求是否因为操作需要按特定顺序执行(这是算法或协议的复杂性),或者因为您将类设计为相互依赖.如果复杂性是由于您的设计,重构可能会消除有序的依赖.

如果你不能重构(复杂性都是必不可少的,你的手上只有一个可怕的协调问题),那么你可以抽象丑陋并保持这个类的用户屏蔽(建造者,工厂,注射器等).

编辑:现在我已经考虑过了,我不相信你的算法或协议的基本复杂性不能被抽象一点(虽然可能是这种情况).根据您的具体问题,可以使用策略模式或观察者模式(事件侦听器)更好地解决这些依赖类操作的相似性.您可能必须将这些类包装在类中,使它们适应与当前公开的接口略有不同的接口.您必须评估让这个怪物类中的代码变得更具可读性(yay)以牺牲项目中多达10个类(boo)的权衡.

我还想提出一个补充,以抽象出这门课的结构.依赖于此类的任何类也使用依赖注入模式似乎很重要.这样,如果您确实使用了构建器,工厂,注射器等,您不会意外地剥夺使用DI模式的一些好处(我认为最重要的是能够替换模拟对象进行测试) .

编辑2(根据您的编辑):

我的第一个想法是"什么,没有记录依赖?" :)

即使知道依赖关系是什么,也很难提供有用的建议.

第一:每个人的责任是什么?为什么这个类依赖于控制器代码(业务逻辑)和模型代码(两个不同的数据库访问类,使用DAO类)?

取决于DAO和DB访问类是代码气味.DAO的目的是什么?DB类的目的是什么?您是否尝试在多个抽象级别进行操作?

OO的原则之一是将数据和行为捆绑到称为类的小东西中.当你创建这个业务逻辑类时,你是否违反了这一点,它与它操作的对象不同于与该类不同的DAO?相关:简要介绍一下SOLID.

第二:加载配置的类.不好闻.依赖注入可帮助您识别依赖关系并将其交换出来.你的怪物类取决于某些参数.这些参数被分组到这个配置类中,因为......?这个配置类的名称是什么?是DBparameters吗?如果是这样,它属于DB对象,而不属于此类.像配置一样通用吗?如果是这样,你就有一个迷你依赖注入器(授权,它可能只注入字符串或int值而不是像类这样的复合数据,但为什么?).尴尬.

第三:我从重构中学到的最重要的一课是我的代码很糟糕.我的代码不仅很糟糕,而且还没有一个转变让它停止吸吮.我所希望的最好的就是让它少吃.一旦我这样做,我可以再次减少吸吮.然后再次.一些设计模式很糟糕,但它们的存在是为了让您的代码可以转换为不那么糟糕的代码.所以你拿走你的全局并让他们成为单身人士.然后你消灭你的单身人士.不要气馁,因为你刚刚重构,发现你的代码仍然很糟糕.它吸少了.因此,您的Configuration加载对象可能会闻到,但您可能认为它不是代码中最神奇的部分.事实上,你可能会发现"修复"它的努力是不值得的.