逻辑层=外观层?

引言:

  在机房收费系统中,因为自己走过的一个‘误区’,体会到了一个很有意思的问题:三层开发中业务逻辑层+外观模式导致业务逻辑层被架空。原本该是各种业务逻辑判断的B层,一下被新加进来的外观层“越俎代庖”了。这样,原本三设计中的核心:业务逻辑层一下成了鲁迅在其文章中所言:有原先殷实的家境一下子家道中落,被外观层所取代。

基础知识:

  业务逻辑层:作为系统架构中体现核心价值的部分,其关注点集中在业务规则的制定、业务流程的实现等与业务需有关的系统设计。主要用于做一些有效性验证的工作,以更好的保证程序运行的健壮性。如完成数据添加、修改和查询业务等;不允许指定的文本框中输入空字符串,数据格式是否正确以及数据类型验证;用户权限的合法性判断等;通过以上的诸多判断以决定是否将操作继续向后传递,尽量保证程序的正常运行(官话)。拿我们机房收费系统来说,是各个用例功能的实现,都应该在B层有对应的实现(经典三层开发状态下而言),这也是我们经常提到的:业务逻辑层尽量向UI层靠近(B层对应D层的也行,各有各的好处吧)


  外观模式:为仔细系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统容易使用;外观模式最大的特点将细粒度的对象包装成粗粒度的对象,应用程序通过访问这个外观对象,来完成细粒度对象的调用。


  我自己的感觉,看着师哥师姐合作版机房收费系统,在用户界面层和数据访问层中间加入了一个外观层,通过这个外观层,来封装UI层单个用例所需要的各个业务逻辑实现,从而减少了UI层和BLL层之间的依赖关系,增强了系统的稳定与健壮性。但是,这样就出现一个问题:一些复杂的业务逻辑实现用外观层可以很便捷的实现,但是一些简单的业务功能如果使用外观,就会产生一种冗余的行为;例如我们如果在D层返回的是一个Datatable 类型的数据,添加外观层之后,因为UI层没有对D层的引用,而是通过外观层来实现的,这样就导致B层会继续想外观层返回一个Datatable类型的数据,B层就是一个简单的过渡?那样要B层做什么?这样的话符合原先三层开发中B层的描述么?


举例说明:

 机房收费系统中,用户退卡业务功能,其大致流程:

  1 判断用户是否注册

  2判断用户是否正在上机

  3只有当用户注册且用户没有上机条件下才可以进行退卡操作:

StuInfoBLL

''' <summary>    ''' 检索学生信息
    ''' </summary>
    ''' <param name="Student"></param>
    ''' <returns>dtStudentInfo</returns>
    ''' <remarks>返回某个注册学生的信息表</remarks>
    Public Function SelectStudent(ByVal Student As Entity.StudentInfoEntity) As DataTable
        Dim dtStudentInfo As DataTable
        IStudentInfo = dataAccess.CreateStudent
        dtStudentInfo = IStudentInfo.SelectStudent(Student)
        Return dtStudentInfo
    End Function

StuOnlineBLL
''' <summary>	    ''' 查询正在上机学生信息
	    ''' </summary>
	    ''' <param name="Student"></param>
	    ''' <returns>dtOnlineStu</returns>
	    ''' <remarks>获取正在上机学生信息</remarks>
	    Public Function SelectOnlineStu(ByVal Student As Entity.StuOnlineEntity) As DataTable
	
	        Dim dtOnlineStu As DataTable
	        IOnline = dataAccess.CreateOnline
	        dtOnlineStu = IOnline.SelectOnlineStu(Student)
	        Return dtOnlineStu
	
	    End Function

CancelCardBLL

 ''' <summary>    ''' 将退卡信息添加到退卡表中
    ''' </summary>
    ''' <param name="CancelCard"></param>
    ''' <returns>Boolean</returns>
    ''' <remarks>如果添加退卡记录成功返回true 否则返回false</remarks>
    Public Function AddCancelCard(ByVal CancelCard As Entity.CancelCardEntity) As Boolean
        Icancelcard = dataAccess.CreateCancelCard
        If Icancelcard.AddCancelCard(CancelCard) = True Then
            Return True
        Else
            Return False
        End If
    End Function
外观层

   Dim bllCancelCard As New BLL.CancelCardBLL '
    Dim bllStuInfo As New BLL.StudentInfoBLL '用来判断用户是否存在,并获取相应的用户信息
    Dim bllOnlineStu As New BLL.OnlineBLL  '用来判断用户是否正在上机
    Dim dtStuInfo As DataTable
    Dim dtOnlineStu As DataTable
''' <summary>
    ''' 退卡外观层
    ''' </summary>
    ''' <param name="CardInfo"></param>
    ''' <returns>Boolean</returns>
    ''' <remarks>1 用户是否存在 2用户是否上机 3 向退卡表中添加结账信息 4 从注册表中删除</remarks>
    Function CancelCard(ByVal CardInfo As Entity.StudentInfoEntity, CancelInfo As Entity.CancelCardEntity) As Boolean
        dtStuInfo = bllStuInfo.SelectStudent(CardInfo)
        '用户尚未注册
        If dtStuInfo.Rows.Count = 0 Then
            Throw New Exception("该账户尚未注册,请选择其它账户进行操作")
        Else
            '用户正在上机
            If dtOnlineStu.Rows.Count = 0 Then
                Throw New Exception("该账户正在上机,请下机之后再进行退卡操作")
            Else
                
                '从注册表中删除成功,并将退卡信息添加到退卡表中成功 返回true 否则 返回false
                CancelInfo.CancelCardID = Trim(CardInfo.StuCardID)
                CancelInfo.CancelCash = dtStuInfo.Rows(0).Item(7)
                CancelInfo.CancelDate = Date.Today
                CancelInfo.CancelTime = TimeOfDay
                CancelInfo.CancelStatus = "已结账"
                CancelInfo.CancelUserID = dtStuInfo.Rows(0).Item(9)
                If bllCancelCard.AddCancelCard(CancelInfo) = True And bllStuInfo.DeleteStudent(CardInfo) = True Then
                    Return True
                Else
                    Return False
                End If
            End If
        End If
End Function

  像这种相对而言,比较复杂的业务实现,用到外观层就会简化很多。如果用户退卡,那么只需要实例化外观层用户退卡类,然后实现其方法就可以了。有利也有‘弊’,假如我要获取用户工作记录这些相对简单的业务功能,就会产生系统资源的浪费;我本来已经从D层返回一个Datatable 类型的值了,但是因为UI层没有相应的引用,导致必须将这个Datatable类型的值再返回到外观层,B层就是简单的过渡,没有任何‘存在感’!对于这个问题,我的想法是:删除外观层,将上述例子中在外观层中的代码实现,转移到B层用户退卡业务逻辑中去;这样对于那些简单的业务逻辑,也就产生传递两次导致系统资源浪费的情况;

个人感觉:

  实际上外观模式,就是一个业务逻辑‘完美’的B层;一个逻辑良好的B层可以也同样是一个完美的外观层。开发人员将B层的业务逻辑设计的很好的,那么不使用外观模式,同样可以对UI层提供一个统一的接口!将UI层所需要的各种方法封装在B层,貌似没有用到外观模式的型,但实质却是外观模式的魂:对上层提供统一的接口而不管具体的复杂业务逻辑实现。当然,一个业务逻辑良好的B层也不是简单容易实现的,这就需要在实践中具体而仔细的琢磨了。(完)

<think>好的,我现在需要回答用户关于C#项目中业务逻辑的最佳实践及存放位置的问题。首先,我得回顾一下用户提供的引用内容,尤其是引用[1]和[3],因为它们提到了分结构和构造函数的设计原则。 根据引用[1],分结构通常分为数据访问业务逻辑和表示,这样做的目的是为了实现高内聚低耦合。用户想知道业务逻辑应该放在哪里,所以应该明确说明它在三架构中的位置,即介于数据访问和表示之间。 接下来,最佳实践方面,引用[3]提到构造函数应保持简单,专注于初始化,而复杂逻辑应放在其他方法中。这可能与业务逻辑中的类设计有关,所以需要强调业务逻辑中的类如何设计,比如使用服务类或领域模型来封装业务规则,保持类的单一职责。 用户可能还关心具体的项目结构,比如在Visual Studio中如何组织项目,可能需要建议创建独立的类库项目来存放业务逻辑,这样便于管理和解耦。同时,需要提到依赖注入的使用,以提高可测试性和松耦合,这符合引用[1]中的低耦合思想。 另外,引用[4]虽然讲的是XAML模板,但提到模板定义外观而不关心具体实现,这可能间接支持分架构中将UI业务逻辑分离的做法,但可能不需要直接引用它。 还需要考虑数据流向,比如业务逻辑如何调用数据访问,而表示如何调用业务逻辑,确保各职责清晰。同时,异常处理、日志记录等横切关注点应该在业务逻辑中如何处理,比如通过中间件或AOP技术,但如果没有相关引用,可能需要避免深入。 最后,根据用户要求生成相关问题,需要围绕分架构、业务逻辑与其他的交互、最佳实践的具体方法等方面提出问题,确保问题相关且有引导性。 总结回答结构:首先明确业务逻辑的位置,然后分点列出最佳实践,包括项目结构、类设计、依赖注入、与上下的交互等,最后附上相关问题。</think>### C#业务逻辑的最佳实践及次结构 在C#分架构中,业务逻辑(Business Logic Layer, BLL)是**三架构的核心中间**,位于数据访问(DAL)和表示UI)之间[^1]。以下是其存放位置和最佳实践的详细说明: --- #### 1. **次结构中的位置** - **典型分**: $$表示UI) \rightarrow 业务逻辑(BLL) \rightarrow 数据访问(DAL)$$ - **数据访问**:直接操作数据库(如通过Entity Framework)。 - **业务逻辑**:封装业务规则、数据验证、计算逻辑等。 - **表示**:处理用户界面(如ASP.NET MVC的Controller或WPF/WinForms界面)。 --- #### 2. **最佳实践** **(1) 独立的类库项目** - 在Visual Studio中,建议将业务逻辑定义为单独的**类库项目**(例如`MyApp.Business`),与UI和数据访问物理分离。 - **优点**:避免代码耦合,便于单元测试和模块复用。 **(2) 职责明确的设计** - **服务类(Service Classes)**:通过`OrderService`、`UserService`等类封装核心业务逻辑(如订单处理、用户权限验证)。 - **领域模型(Domain Models)**:使用POCO(Plain Old CLR Object)类表示业务实体(如`Product`、`Invoice`),避免与数据模型直接绑定。 - **示例代码**: ```csharp public class OrderService { private readonly IOrderRepository _repository; // 依赖注入提升可维护性[^3] public OrderService(IOrderRepository repository) { _repository = repository; } // 业务逻辑:验证并创建订单 public void CreateOrder(Order order) { if (order.TotalAmount <= 0) throw new InvalidOperationException("订单金额无效"); _repository.Add(order); } } ``` **(3) 依赖注入(Dependency Injection)** - 通过接口(如`IOrderRepository`)解耦业务逻辑与数据访问,避免直接依赖具体实现。 - 使用框架(如ASP.NET Core的内置DI容器)管理依赖关系。 **(4) 与上下的交互** - **调用数据访问**:通过仓储模式(Repository Pattern)访问数据,例如: ```csharp public interface IOrderRepository { void Add(Order order); Order GetById(int id); } ``` - **服务表示**:向UI提供业务方法(如`GetUserDashboardData()`),返回DTO(Data Transfer Object)而非原始实体。 **(5) 验证与异常处理** - 在BLL中集中处理业务规则验证(如库存检查、用户权限),并抛出明确的业务异常(如`InsufficientStockException`)。 - 避免在构造函数中实现复杂逻辑,保持初始化过程简单。 --- #### 3. **典型项目结构** ``` MyApp.sln ├─ MyApp.Web (表示:ASP.NET Core MVC) ├─ MyApp.Business (业务逻辑:类库) ├─ MyApp.Data (数据访问:类库) └─ MyApp.Core (公共模型/接口:类库) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值