7

请看下面的代码:

Imports Microsoft.VisualBasic

Public Class PersonBLL
    Private Name As String
    Private Age As Integer

    Dim objPersonDAL As New PersonDAL
    Dim objPerson As Person

    Public Sub getPersonByID()
        objPerson = objPersonDAL.getPersonByID()
        MsgBox(objPerson.Name)
    End Sub
End Class

Public Class PersonDAL
    Private Name As String
    Private Age As Integer

    Public Function getPersonByID() As Person
        'Connect to database and get Person.  Return a person object
        Dim p1 As New Person
        p1.Name = "Ian"
        p1.Age = 30
        Return p1
    End Function
End Class

Public Class Person
    Private _Name As String
    Private _Age As Integer

    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Public Property Age() As Integer
        Get
            Return _Age
        End Get
        Set(ByVal value As Integer)
            _Age = value
        End Set
    End Property
End Class

PersonBLL 调用 PersonDAL 并返回一个 Person 对象。这是正确的方法吗?即,我已经确定了一个持久类并创建了一个相应的 DAL 类,该类具有用于访问数据和返回 Person 对象的函数。

有评论指出这个问题是“主观的”。我同意这一点。我意识到设计取决于项目的要求。是否有任何记录用于设计类似于 SOLID(单一责任等)等的 DAL 的原则?

4

1 回答 1

6

是的,您的问题展示了一种将逻辑分层的非常干净的方法。该类PersonBLL将是业务层的PersonDAL一部分,该类将是数据访问层的Person一部分,该类将是数据传输对象 (DTO) 层的一部分。这是一种非常常见的分离图层的方法,在许多情况下都可以很好地工作。

我唯一的建议是:

  • 你应该把每一层放在他们自己的命名空间中,如果不是他们自己的类库项目的话。
  • 您不应显示来自业务层的消息框。我假设您这样做只是为了演示,但以防万一,我想我应该提一下。显示消息框应该是 UI 层的一部分。例如,如果您PersonBLL.getPersonByID从 Windows 服务或 Web 服务调用,则显示消息框是完全不合适的。
  • 通常,所有方法都是 PascalCase,而不是 camelCase。有些人更喜欢将私有方法设为驼峰式,但公共方法当然不应该是驼峰式。
  • 考虑使用依赖注入技术 (DI) 将数据访问对象注入业务对象。

依赖注入

以下是如何使用 DI 技术执行此操作的示例:

Public Class BusinessFactory
    Public Function NewPersonBusiness() As IPersonBusiness
        Return New PersonBusiness(New PersonDataAccess())
    End Function
End Class

Public Class PersonBusiness
    Implements IPersonBusiness

    Public Sub New(personDataAccess As IPersonDataAccess)
        _personDataAccess = personDataAccess
    End Sub

    Private _personDataAccess As IPersonDataAccess

    Public Function GetPersonByID() As PersonDto Implements IPersonBusiness.GetPersonByID
        Return _personDataAccess.GetPersonByID()
    End Sub
End Class

Public Interface IPersonBusiness
    Function GetPersonByID() As PersonDto
End Interface

Public Interface IPersonDataAccess
    Function GetPersonById() As PersonDto
End Interface

Public Class PersonDto
    Private _name As String
    Private _age As Integer

    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Property Age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal value As Integer)
            _age = value
        End Set
    End Property
End Class

这样做有很多好处。您可以拥有多个可互换的数据访问层实现,因此更加灵活。此外,当您想要对业务类进行单元测试时,您可以注入一个虚假的数据访问对象。DI 设计避免了许多导致错误、意大利面条式代码的陷阱。

对于 DI,通常建议您将依赖对象作为接口而不是具体类型(例如IPersonDataAccess,而不是PersonDataAccess)。这样做可能有点麻烦,但你很快就会习惯它。由于您通常在那时为每个类创建一个接口,因此将接口放在与类相同的代码文件中会很方便。因此,例如,PersonBusiness.vb 将包含PersonDataAccess类和IPersonDataAccess接口。

对依赖项使用接口而不是类很重要的原因有两个:

  1. 它确保了设计的灵活性。您希望能够覆盖依赖类型的每个公共成员,以便您可以创建任何类型的具体实现。还有其他方法可以做到这一点。例如,您可以通过简单地使用修饰符IPersonDataAcess标记类中的每个公共属性和方法来跳过创建接口,但没有什么强迫您这样做。即使您一直记得这样做,但这并不意味着其他处理您的代码的人会知道他们应该这样做。PersonDataAccessOverrideable

    DI 通常与单元测试联系在一起,因为它是确保代码可测试的最佳工具。在进行单元测试时,特别重要的是您能够覆盖依赖类型中的任何成员,这样您就可以制作一个“假”对象,该对象按照您需要的方式工作,以便正确执行单元测试。这些“假”对象称为mocks

  2. 你在技术上对你的依赖更加诚实。实际上,您并不是真的说您的依赖项实际上是PersonDataAccess该类的一个实例。实际上,您的依赖项是碰巧具有相同公共接口的任何对象。通过询问课程,您暗示您需要特定的实现,这是一个谎言。如果你设计得当,你只关心接口是否相同,所以只询问接口本身,你就是在精确指定你要指定的内容:)

于 2013-01-01T18:08:30.353 回答