0

我有一个业务层,只有一个类应该对外部世界可见。因此,我已将除该类之外的所有类标记为内部类。由于该类需要一些内部类来实例化,因此我需要将其他类标记为公共,而其他类依赖于其他一些类等等。所以最终我几乎所有的内部课程都被公开了。

您如何处理此类情况?

同样今天只有一个类暴露在外部世界,但将来可能会有两三个,所以这意味着我需要三个门面?

谢谢

4

4 回答 4

2

正确,您注入的所有依赖项都必须对您的组合根可见。听起来你在问这个问题:Ioc/DI - 为什么我必须在入口应用程序中引用所有层/组件?

引用 Mark Seeman 的部分答案:

您不必为所有必需的库添加硬引用。相反,您可以以基于约定的程序集扫描(首选)或 XML 配置的形式使用后期绑定。

还有这个,来自史蒂文:

如果您对使用程序集保护您的架构边界非常严格,您可以简单地将您的合成根移动到一个单独的程序集中。

但是,您应该问自己为什么这样做是值得的。如果只是为了强制执行架构边界,那么纪律是无可替代的。我的经验是,当遵循 SOLID 原则时,该学科也更容易维护,其中依赖注入是“粘合剂”。

于 2013-07-19T16:39:46.897 回答
2

经过大量研究后,我正在写下我的发现,以便对依赖注入的新手有所帮助


关于我目前的设计和依赖注入的误解:

初始方法和与之相关的问题:

我的业务层内部有一个组合根,它应该在业务层之外并靠近应用程序入口点。在作曲根中,我基本上有一个大工厂,被 Mark Seemann称为Poor Man's DI 。在我的应用程序起点中,我创建了这个工厂类的一个实例,然后创建了我唯一(打算成为)对外界可见的类。这个决定显然违反了Liskov 的原则,即每个依赖项都应该是可替换的。我有一个模块化设计,但我以前的方法是紧密耦合的,尽管只有一些代码清洁度和代码可维护性,但我无法从中获得更多好处。

更好的方法是:

Facio Ratio 提供的一个非常有用的链接

组合根应该在应用程序根附近,所有依赖类都应该公开,我最初认为这是一个问题;让它们公开我正在引入低耦合并遵循 Liskov 的替换,这很好。

于 2013-08-31T13:45:58.407 回答
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
{

}
于 2013-07-19T04:32:18.543 回答
0

我可以看到三个解决方案,没有一个很好。您可能想以某种方式组合它们。但...

首先,在构造函数中放置一些简单的参数(也许是数字),让调用者说出他想要做什么,并且新的公共类实例可以用来获取内部类对象(自我注入)。(您也可以使用专门用于在此处传达信息的特殊公共类/接口。)这使得接口变得笨拙且受限,但非常适合封装。如果调用者更喜欢添加一些快速参数来构造复杂的可注入对象,那么这可能会很好。(当一个方法需要五个您以前从未听说过的类对象时,当您需要甚至想要的唯一选项是“只读”与“可编辑”时,这总是一个拖累。)

其次,您可以公开您的内部课程。现在调用者拥有巨大的力量,可以做任何事情。如果调用代码确实是系统的核心,这很好,但如果您不太信任该代码,或者如果调用者真的不想被所有挑剔的细节打扰,那就不好了。

第三,您可能会发现可以将调用代码中的一些类提取到您的程序集中。如果你真的很幸运,打电话的班级可能会在内部工作得更好(希望不要在更高一级重新引入这个问题)。

回复评论:

据我了解,您有一个服务调用业务层的公共类中的方法。要进行调用,它需要业务层中其他类的对象。这些其他类是并且应该是内部的。例如,您想调用一个名为 GetAverage 的方法并将(内部)类 RoundingPolicy 的实例传递给它,以便它知道如何舍入。我的第一个答案是你应该取一个整数值而不是一个类:一个常量值,例如 ROUND_UP、ROUND_DOWN、NEAREST_INTEGER 等。然后 GetAverage 将使用这个数字在业务层内生成正确的 RoundingPolicy 实例,保持 RoundingPolicy 内部。

我的第一个答案是我建议的答案。但是,它为服务提供了一个相当原始的接口,因此我的后两个答案提出了替代方案。

第二个答案实际上是您要避免的。我的想法是,如果服务需要所有这些内部类那么问题可能就没有办法解决了。在我上面的示例中,如果服务在传递它之前使用 30 行代码来构造正确的 RoundingPolicy 实例,那么您不会只用几个整数参数来解决问题。您需要对整体设计进行大量思考。

第三个答案是一个绝望的希望,但您可能会发现调用代码正在执行可以在业务层内轻松完成的工作。这实际上与我的第一个答案相似。然而,这里的界面可能更优雅。我的第一个答案限制了该服务可以做什么。这个答案表明该服务无论如何都不想做太多事情。它始终使用一个相同的 RoundingPolicy 实例,因此您甚至不需要传递参数。

我可能不完全理解你的问题,但我希望这里有一个你可以使用的想法。

还有更多:第四个答案:

我认为这是我的第一个答案的一部分,但我已经深思熟虑并认为我应该明确说明。

我认为您调用的类不需要接口,但您可以为所有不想向服务公开的类创建接口。例如,IRoundingPolicy。您将需要一些方法来获取这些接口的真实实例,因为new IRoundingPolicy()它不起作用。现在,该服务暴露于您试图隐藏的类的所有复杂性(下侧),但他们无法看到类内部(上侧)。您可以准确控制服务可以使用的内容——原始类仍然被封装。这也许是我第二个答案的可行版本。这在服务需要比我的第一个答案允许的更精细选项的一两个地方可能很有用。

于 2013-07-19T20:16:53.287 回答