我有一个业务层,其中只有一个类应该对外部世界可见。所以,我已将所有类标记为内部除了该类。由于该类需要一些内部类来实例化,我需要将其他类标记为public,其他类依赖于其他类等等。所以最终几乎所有的内部课程都被公开了。
你如何处理这种情况?
今天也有一个课程暴露在外面的世界,但将来可能会有两三个,所以这意味着我需要三个立面?
由于
答案 0 :(得分:2)
正确,所有注入的依赖项必须对您的Composition Root可见。听起来你问的是这个问题:Ioc/DI - Why do I have to reference all layers/assemblies in entry application?
引用Mark Seeman的部分回答:
您不必为所有必需的库添加硬引用。相反,您可以使用基于约定的汇编扫描(首选)或XML配置的形式进行后期绑定。
这也是史蒂文:
如果您非常严格地使用装配保护您的建筑边界,您只需将您的组合根移动到单独的组件。
但是,你应该问问自己为什么这样做是值得的。如果它只是为了强制建筑界限,那么就没有替代纪律。我的经验是,遵循SOLID原则也更容易维护该规则,依赖注入是“粘合剂”。
答案 1 :(得分:2)
经过大量的研究后,我正在撰写我的研究结果,这对依赖注射的新手可能有所帮助
<小时/> 关于我当前设计和依赖注入的误解:
初步方法和与之相关的问题:
我的业务层内部有一个组合根,其中包含 它应该在业务层之外并且靠近应用程序 入口点。在作品根目录中,我基本上有一个名为Poor Man's DI by Mark Seemann的大工厂。在我的应用程序起点中,我创建了这个工厂类的实例,然后创建我唯一的(打算是)可见的类到外部世界。这个决定显然违反了Liskov's Principle,它表示每个依赖都应该是可替换的。我采用模块化设计,但我之前的方法是紧密耦合的,尽管只有一些代码清洁度和代码可维护性,但我无法从中获得更多好处。
更好的方法是:
A very helplful link given by Facio Ratio
组合根应该在应用程序根目录附近,所有依赖类都应公开,我最初称之为问题;将它们公之于众我正在引入低耦合并遵循Liskov的替换,这很好。
答案 2 :(得分:1)
您可以将公共类更改为接口,程序的所有其他部分只能知道接口。以下是一些示例代码来说明这一点:
public interface IFacade
{
void DoSomething();
}
internal class FacadeImpl : IFacade
{
public FacadeImpl(Alfa alfa, Bravo bravo)
{
}
public void DoSomething()
{
}
}
internal class Alfa
{
}
internal class Bravo
{
}
答案 3 :(得分:0)
我可以看到三种解决方案,没有真正的好处。您可能希望以某种方式将它们组合在一起。但...
首先,在构造函数中放置一些简单的参数(可能是数字),让调用者说出他想要做的事情,并且新的公共类实例可以用来获取内部类对象(自注入)。 (您可以使用专门用于传达信息的公共类/接口。)这会产生一个笨拙且有限的接口,但对于封装非常有用。如果调用者更喜欢添加一些快速参数来构建复杂的可注射对象,那么这可能会很好。 (当一个方法想要你以前从未听过的五个类对象时,你需要或者甚至想要的唯一选项是“只读”和“可编辑”,这总是一个拖累。)
其次,您可以将内部课程公开。现在呼叫者拥有巨大的力量,可以做任何事情。如果调用代码确实是系统的核心,这是很好的,但如果您不完全信任该代码,或者调用者真的不想被所有挑剔的细节所困扰,那就太好了。
第三,您可能会发现可以将一些类从调用代码中提取到程序集中。如果你真的很幸运,打电话的班级可能会在内部工作得更好(希望没有重新将这个问题重新引入一级)。
对评论的回应:
据我所知,您有一项服务在业务层的公共类中调用方法。要进行调用,它需要业务层中其他类的对象。这些其他类是并且应该是内部的。例如,您想要调用一个名为GetAverage的方法,并将其传递给(内部)类RoundingPolicy的实例,以便它知道如何舍入。我的第一个答案是你应该取一个整数值而不是一个类:一个常量值,如ROUND_UP,ROUND_DOWN,NEAREST_INTEGER等。然后,GetAverage将使用此数字在业务层内生成正确的RoundingPolicy实例,保持RoundingPolicy内部。
我的第一个答案是我建议的那个。但是,它为服务提供了一个相当原始的界面,所以我的第二个答案提出了替代方案。
第二个答案实际上就是你要避免的。我的想法是,如果服务需要所有这些内部类,那么可能无法解决问题。在上面的示例中,如果服务使用30行代码在传递之前构造正确的RoundingPolicy实例,那么您不会仅使用几个整数参数来解决问题。你需要对整体设计进行深思熟虑。
第三个答案是一个绝望的希望,但你可能会发现调用代码正在做一些可以在业务层内轻松完成的工作。这实际上与我的第一个答案类似。然而,在这里,界面可能更优雅。我的第一个答案限制了服务的功能。这个答案表明服务不想做太多事情;它总是使用一个相同的RoundingPolicy实例,因此您甚至不需要传递参数。
我可能不完全理解你的问题,但我希望你可以在某处使用这个想法。
更多:第四回答:
我认为这是我的第一个答案的一部分,但我已经考虑过了,并认为我应该明确说明。
我不认为您正在进行调用的类需要一个接口,但您可以为您不希望向服务公开的所有类创建接口。例如,IRoundingPolicy。您将需要一些方法来获取这些接口的实际实例,因为new IRoundingPolicy()
不起作用。现在,该服务暴露于您试图隐藏的类的所有复杂性(向下),但他们无法在类中看到(向上)。您可以准确控制服务的工作内容 - 原始类仍然是封装的。这可能是我的第二个答案的可行版本。这个可能在一两个地方很有用,在这两个地方,服务需要比我的第一个答案允许的更精细的选项。