将BLL(业务逻辑层)分解为BLL和DAL(数据访问层)

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的原则文件记录?


“正确的方法”有点主观。 “这是一个有效的方法吗?”是可以回答的问题 - 我会说“是”。 - David
@David Stratton,我已经更新了问题。您知道是否有像SOLID这样的原则特别适用于设计数据访问层吗? - w0051977
好的编辑。我会让比我聪明的人来回答这个问题。这不是我的专长领域,而且这个网站上的大多数人都比我聪明得多。我可能会给你错误的指导。我能提供的只是我之前见过这种设计,来自非.NET背景的人 - 这似乎很常见。我个人觉得这很繁琐,但就像我说的,我不是最聪明的人。 - David
1个回答

6
是的,你的问题展示了一种将逻辑分离到不同层级的非常干净的方式。 PersonBLL 类将成为业务层的一部分,PersonDAL 类将成为数据访问层的一部分,而Person 类将成为数据传输对象(DTO)层的一部分。这是一种非常常见的方式来分离你的层级,在许多情况下都是有效的。
我的唯一建议是:
- 您应该将每个层级放在自己的命名空间中,如果不是还应该有自己的类库项目。 - 不应该从业务层显示消息框。我假设您只是出于演示目的这样做的,但以防万一,我认为我应该提一下。显示消息框应该是 UI 层的一部分。例如,如果您从 Windows 服务或 Web 服务调用 PersonBLL.getPersonByID,那么显示消息框将是完全不恰当的。 - 通常,所有方法都是 PascalCase,而不是 camelCase。有些人喜欢将私有方法命名为 camel case,但是公共方法肯定不应该是 camel case。 - 考虑使用依赖注入技术(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设计避免了很多导致代码混乱的陷阱。
使用DI时,通常建议您将依赖项对象请求为接口,而不是作为具体类型(例如,IPersonDataAccess而不是PersonDataAccess)。这样做可能有点麻烦,但您很快就会习惯。由于通常在那一点上为每个类创建一个接口,所以将接口放在与类相同的代码文件中很方便。因此,例如,PersonBusiness.vb将包含PersonDataAccess类和IPersonDataAccess接口。
存在以下两个原因说明为何将接口用于依赖关系比使用类重要:
1. 它确保设计具有灵活性。您希望能够覆盖每个依赖项类型的公共成员,以便您可以创建任何类型的具体实现。还有其他方法可以实现此目标。例如,您可以跳过创建IPersonDataAcess接口,而是直接在PersonDataAccess类中使用Overrideable修饰符标记公共属性和方法,但并没有强制您这样做。即使您始终记得这样做,也不能保证其他人在处理您的代码时知道他们应该这样做。
2. 您更加真实地表述了依赖项。实际上,您并不是说您的依赖项实际上是PersonDataAccess类的一个实例。事实上,您的依赖项是具有相同公共接口的任何对象。通过请求这个类,您暗示需要特定的实现,这是一种谎言。如果您设计良好,那么只关心接口是否相同,因此仅请求接口本身,可以准确指定您的意图 :)

谢谢。+1推荐DI。你会为所有类创建接口吗?PersonDto在哪里定义?我假设它与我的Person类相同? - w0051977
是的,在我的示例中,PersonDto 被假定为和你问题中的 Person 类是一样的。抱歉给你带来困惑。是的,我建议为所有非 DTO 类创建接口。如果你制作了一个只适用于一个类的接口,我建议将其放在同一个代码文件中。因此,例如,PersonBusiness.vb 将包含 PersonBusiness 类和 IPersonBusiness 接口。 - Steven Doggart
我喜欢将类文件和接口放在同一个文件中的想法,因为这样可以使项目更加清晰。我并不完全理解接口的好处。如果需要对系统进行更改,那么肯定需要进行更改——无论你是否使用接口,但是我在网上搜索时似乎到处都在建议使用接口。在我接受你的答案之前,你能给我举个例子吗?谢谢。 - w0051977
我修改了我的回答。如果您有更多的问题,请告诉我。 - Steven Doggart
如果没有业务逻辑,而你的业务方法最终只是一个传递调用到数据访问层(DAL),我仍然认为有 BLL 包装 DAL 是很重要的。当然,它并没有添加任何直接价值,但它使代码更具灵活性,以便于未来更改 DAL 到不同技术或将应用程序拆分成不同层次,如果代码已经正确分层。此外,即使当前不需要任何业务逻辑,也不意味着您不会在以后发现该方法需要一些逻辑。 - Steven Doggart
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接