2

我一直在阅读关于 DI 的内容,据我所知,您的 VS 解决方案的结构应如下所示:

Web (UI) 项目参考:

  • 数据访问(*从头开始)
  • 商业逻辑
  • DTO

业务逻辑项目参考:

  • DTO

数据访问项目参考:

  • 网络(*又挠头)
  • 商业逻辑
  • DTO

此外,具体实现的接口应保存在业务逻辑项目中,DA 将在具体类中实现该项目。

“经典”的 3 层结构是:

网络参考

  • 商业逻辑

业务逻辑参考:

(使用 DTO 引用所有层)。

我试图用 DI 结构来理解的是,我很欣赏它似乎有助于分离每个模块以进行测试,而不是在类中调用具体类 - 但是通过在项目中设置引用的方式,感觉就像有与层的紧密耦合?UI 对 DA 和 BL 都有硬引用(因此必须实例化 BL 类和实现 BL 作为构造函数注入的一部分接受的接口的 DA 类)。

UI 现在同时引用了 BL 和 DA,感觉有点不对劲。如果我想说使用 SendMessage() 方法实现 IMessage(从 SMTP 服务器交换为 SMS 提供者),我仍然必须在 UI 中调用 DA 类并将其传递给 BL。感觉怪怪的?!

看起来 UI 通过调用 BL 逻辑类的实例来决定它想要的数据实现,该实例接受 Web 层中数据访问类的具体实现?

我只是想完全清除 VS 中经典的 n 层结构的头脑,并对在 UI 中处理它的方式持开放态度(UI 应该只担心 UI 对吗?)。我想我只需要通过一个简单的解释让那个灯泡熄灭。如果您能提供帮助,将不胜感激!

PS - 目前我正在阅读 Mark Seemann 的 .NET 中的依赖注入一书,所以我有点头疼!

4

2 回答 2

0

您可以将接口与其实现分开,方法是将它们放在不同的程序集中,并将所有 DI 配置放在单独的程序集中。如果您将所有 DI 配置放在不同的程序集中,那么您的 UI 层将不再需要引用所有其他层。然后您还可以轻松切换配置程序集以快速更改 DI 配置而不影响 UI 层。

于 2014-02-11T12:08:47.830 回答
0

这完全取决于您将工厂放在哪里以及您对每个工厂承担多少责任。例如,如果您有以下工厂

' In the data access layer project
Public Class DataAccessFactory()
    Implements IDataAccessFactory

    Public Sub New()
        ' ...
    End Sub

    ' ...
End Class

' In the business layer project
Public Class BusinessFactory()
    Public Sub New(daFactory As IDataAccessFactory)
        ' ...
    End Sub

    ' ...
End Class

现在,当您在 UI 中创建业务工厂时,您首先必须在 UI 层创建一个数据访问工厂,以便您可以将其注入业务工厂。因此,在这种情况下,UI 层需要同时引用业务和数据访问层项目。但是,你也可以给业务工厂更多的责任,像这样:

Public Class BusinessFactory()
    Public Sub New()
        _daFactory = New DataAccessFactory()
    End Sub

    Private _daFactory As IDataAccessFactory

    ' ...
End Class

现在,UI 层无法决定将使用哪个数据访问层,但它也不再需要对数据访问层项目的引用。在第二个示例中,只有业务层项目需要对数据访问层项目的引用。

它不太灵活,因为您需要为要支持的每个数据访问工厂实现一个单独的业务工厂,但是您可以通过如下方式实现它来使其更加灵活:

Public Class BusinessFactory()
    Implements IBusinessFactory

    Public Sub New()
        _daFactory = NewDataAccessFactory()
    End Sub

    Private _daFactory As IDataAccessFactory

    Protected Overridable Function NewDataAccessFactory() As IDataAccessFactory
        Return New DataAccessFactory()
    End Function

    ' ...
End Class

请注意,该NewDataAccessFactory方法是可覆盖的。所以现在,当您需要使用不同的数据访问工厂时,您可以覆盖相同的方法,而无需复制工厂逻辑的其余部分。例如:

Public Class XmlBusinessFactory()
    Inherits BusinessFactory

    Public Sub New()
        MyBase.New()
    End Sub

    Protected Overridable Function NewDataAccessFactory() As IDataAccessFactory
        Return New XmlDataAccessFactory()
    End Function
End Class

当然,所有逻辑(非工厂)类仍然是完全可注入的。只有工厂类的灵活性稍差。但是,从好的方面来说,您的 UI 层不仅不再需要引用数据访问层,而且还使您的工厂对象的实例化变得更加容易。您需要做的就是创建您需要的工厂,而不是创建它的所有依赖工厂。

如果您需要使工厂的依赖项可注入,例如对工厂进行单元测试,您仍然可以通过创建可注入的派生类来实现,如下所示:

Public Class InjectableBusinessFactory()
    Inherits BusinessFactory

    Public Sub New(daFactory As IDataAccessFactory)
        MyBase.New()
        _daFactory = daFactory
    End Sub

    Private _daFactory As IDataAccessFactory

    Protected Overridable Function NewDataAccessFactory() As IDataAccessFactory
        Return _daFactory
    End Function
End Class

所以,虽然这样做需要额外的步骤,但这确实意味着遵循这个设计并不意味着你把自己画到一个角落里。最后,您需要权衡利弊并选择最适合您的情况。我建议尝试以两种方式实现您的工厂,以便您对这两种方法都有感觉。

于 2014-01-25T18:58:46.283 回答