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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14112392/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 11:43:47  来源:igfitidea点击:

Breaking BLL (Business Logic Layer) to BLL and DAL (Data Access Layer)

vb.netdesign-patterns

提问by w0051977

Please see the code below:

请看下面的代码:

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 calls PersonDAL and returns a Person object. Is this the correct approach? i.e. I have identified a persistent class and created a corresponding DAL class with a function for accessing the data and returning the Person object.

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

There is a comment that states that this question is "subjective". I agree with this. I realise that the design depends on the requirements of the project. Are there any principles documented for designing a DAL similar to SOLID (single responsibility etc) etc.

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

采纳答案by Steven Doggart

Yes, your question demonstrates a very clean way to separate the logic into layers. The PersonBLLclass would be part of the business layer, the PersonDALclass would be part of the data access layer, and the Personclass would be part of the data transfer objects (DTO) layer. This is a very common way to separate your layers which works well in many situations.

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

My only recommendations would be:

我唯一的建议是:

  • You should put each layer in their own namespaces, if not also their own class library projects.
  • You should not show a message box from the business layer. I assume you only did this as a means of demonstration, but just in case, I thought I should mention it. Showing a message box should be part of the UI layer. For instance, if you were calling PersonBLL.getPersonByIDfrom a windows service or a web service, showing a message box would be entirely inappropriate.
  • Typically, all methods are PascalCase, not camelCase. Some people prefer to make private methods camel case, but certainly public methods shouldn't be camel case.
  • Consider using dependency-injection techniques (DI) to inject the data access object into the business object.
  • 你应该把每一层放在他们自己的命名空间中,如果不是还有他们自己的类库项目。
  • 您不应显示来自业务层的消息框。我假设你这样做只是为了演示,但为了以防万一,我想我应该提到它。显示消息框应该是 UI 层的一部分。例如,如果您PersonBLL.getPersonByID从 Windows 服务或 Web 服务调用,则显示消息框是完全不合适的。
  • 通常,所有方法都是PascalCase,而不是camelCase。有些人更喜欢将私有方法设为驼峰式,但当然公共方法不应该是驼峰式。
  • 考虑使用依赖注入技术 (DI) 将数据访问对象注入业务对象。

Dependency Injection

依赖注入

Here's an example of how to do this with DI techniques:

以下是如何使用 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

Doing it this way has many advantages. You can have multiple interchangeable data access layer implementations, so it's more flexible. Also, you can inject a fake data access object when you want to unit test the business class. DI design avoids many of the traps that lead to buggy, spaghetti code.

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

With DI, it is typically recommended that you ask for dependency objects as an interface rather than as a concrete type (e.g. IPersonDataAccessrather than PersonDataAccess). Doing so can be a little bit of a hassle, but you get use to it quickly. Since you are often, at that point, creating one interface for every class, it's convenient to just put the interface in the same code file as the class. So, for instance, PersonBusiness.vb would contain both the PersonDataAccessclass and the IPersonDataAccessinterface.

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

There are two reasons why using interfaces, rather than classes, for your dependencies is important:

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

  1. It ensures that the design is flexible. You want to be able to override every public member of the dependency type so that you can create any kind of concrete implementation. There are other ways to do this. For instance, you could skip creating the IPersonDataAcessinterface by simply marking every public property and method in the PersonDataAccessclass with the Overrideablemodifier, but there's nothing forcing you to do that. Even if you always remembered to do so, that doesn't mean someone else working on your code would know they should do that.

    DI is often tied-in with unit testing because it is the best tool available for ensuring that code is testable. When unit testing, it is particularly important that you are able to override ever member in a dependency type so you can make a "fake" object that works just the way you need it to work in order to properly perform the unit test. These "fake" objects are called mocks.

  2. You are being more technically honest about what your dependency is. In reality, you aren't really saying that your dependency is actually an instance of the PersonDataAccessclass. In actuality, your dependency is any object that happens to have that same public interface. By asking for the class, you are implying that you need a particular implementation, which is a lie. If you have designed it properly, you only care about the interface being the same, so by asking only for the interface itself, you are specifying precisely what you mean to specify :)

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

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

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