65.9K
CodeProject 正在变化。 阅读更多。
Home

分离域与表示 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (6投票s)

2009 年 7 月 17 日

CDDL

6分钟阅读

viewsIcon

14032

如何分离域与表示。

来自 IRefactor 的转载

在软件开发人员手中,Visual Studio 可以成为一把瑞士军刀。

您想要几个小时内就能有一个功能齐全的应用程序?没问题,先生!

  • 创建一个 Windows Forms 应用程序。
  • 添加相关的数据源(如下图 1 所示:Courses 表)。
  • 将生成好的实体从“数据源”拖放到相应的窗体上。
  • 添加最少的代码来按所需顺序显示窗体。
  • 编译并执行。

Voilà!我向您展示一个“企业级应用程序”!

虽然它看起来很业余,但它允许实现功能齐全的 CRUD 操作。

图 1

但是,权力可能导致腐败……

最近,我遇到了一些使用上述方法实现的rible项目。不用说,这些项目都是“意大利面条式代码”——所有的 UI 元素、视觉状态、业务逻辑和数据访问都集中在几个窗体中。

(别以为这是罕见情况!有很多公司试图用这种“快速而肮脏”的方法来“节省”时间。)

我是一个务实的软件工程师,我相信“快速而肮脏”的方法在某些情况下至关重要。

如果您要演示一个展示您能力的演示应用程序;请随意使用必要的工具,尽可能快地实现它。

然而,如果您使用上述方法开发一个实际的应用程序,那将是一条通往地狱的捷径。慢慢地,但肯定地,“快速开发”会花费越来越多的时间。

  • 客户的按钮点击事件与员工的事件点击非常相似,但会关联和更新不同的 UI 元素——您会在两个地方重复相同的逻辑。
  • 新需求为某个向导流程添加了新条件——您会添加一个“if”语句来处理这些条件。有时,由于已经存在许多“if”语句,您会添加一个“switch”语句。
  • 新需求要求保存更多数据——您会为方法添加更多参数。您甚至会进一步将这些参数添加到每个需要更改的地方(窗体/方法)。
  • 类和方法变得越来越庞大,充斥着 UI 元素、视觉状态转换和业务逻辑。下次尝试阅读代码时,您需要至少几个小时来集中精力梳理一团乱麻。

负责上述项目的项目经理告诉我:

“我想要的任何变更请求,都带有不切实际的时间估算。当我问开发人员为什么需要这么长时间时,他回答:我的天啊——您知道修改那段代码需要多大的工作量吗?您明白我需要多长时间来重新测试应用程序,仅仅是为了验证我没有搞砸任何东西吗?”

看起来很明显,如果项目一开始就按照 UI、业务逻辑和数据访问层清晰分离的方式编写,那么维护产品生命周期会更容易。但显然情况并非如此,那么我们能做什么呢?

我们可以应用的一个工具是:重构

重构是在不改变软件外部行为的情况下,改进其内部结构的进程。通过执行自动单元测试,重构确保了外部行为的完整性。我们以渐进的小步骤(重构步骤)改进代码,每次更改后都进行编译和单元测试。

在接下来的几篇文章中,我将演示一种称为“分离领域与表现”的重构,该重构显然可以打破 UI 和 BL 之间的紧密耦合。在完成重构过程本身的演示后(这需要我几篇文章),我还会解释如何创建单元测试套件,以验证外部行为的完整性。

备注

  • 我将重构图 1(上图)中的应用程序。

    该应用程序包含一个视图(FrmMain)和一个领域对象(CoursesDS)。

  • 虽然示例应用程序包含一个 Form 和一个 DataSet,但相同的技术也可应用于更复杂的情况。只需识别彼此适合的对象组,并为每个组应用以下分离步骤。
  • 建议下载示例IRefactor.CoursesView项目,并在查看代码的同时,通过重构步骤进行操作。
  • 在接下来的文章中,我将介绍如何通过向 MVP(或 MVC)模式进行重构来增强这种分离。
  • 写关于重构过程比实际实现它更难。不要被吓倒,解释每一个小步骤(并提供截图)比实际操作需要更长的时间。分离领域与表现(直到 MVP)大约需要我 10 分钟的工作。

重构步骤

  • 当最初定义 Irefactor.CoursesView 项目时,Visual Studio 为 Courses 数据源生成了一个名为 CoursesDSDataSet。正如您所见,Irefactor.CoursesView 项目混合了 UI(FrmMain)元素和领域(CoursesDS)元素。
    图 2

  • 让我们开始将 CoursesDS 领域对象从 UI(View)中移除。

    创建一个项目 IRefactor.Common,并将 CoursesDS DataSet 复制到其中。

    (将 CoursesDS 从一个项目拖到另一个项目,Visual Studio 会处理剩余的。)

    由于 CoursesDS 是 VS 自动生成的类,因此还应复制存储在 IRefactor.CoursesView 项目设置中的连接字符串定义。将 Settings.settings 添加到 IRefactor.Common Properties 文件夹,并复制连接字符串。

    图 3

  • 编译解决方案并执行单元测试。
  • 更改 FrmMainView)以使用新创建的 IRefactor.Common.CoursesDS DataSet

    打开 FrmMain 的设计器。移除 coursesDS(领域对象的实例:IRefactor.CoursesView.CoursesDS DataSet – 见红色箭头 1)。

    图 4

  • 在“工具箱”窗口中,在最近添加的 IRefactor.Common 组件下,您将看到 IRefactor.Common.CoursesDS DataSet

    将此 CoursesDS 拖放到 FrmMain 窗体上。

    图 5

  • IRefactor.Common.CoursesDS DataSet 拖放到 FrmMain 界面上会生成该类的实例。将其重命名为 coursesDS(以匹配之前的名称)。
  • IRefactor.CoursesView.CoursesTableAdapter 重复最后三个步骤(参见图 4,红色箭头 2)。
  • 选择 BindingSource(参见图 4,bsCourses),并将其 DataSource 更改为 coursesDS 实例(coursesDS 现在来自 IRefactor.Common 项目)。还要将其 DataMember 更改为指向 Courses 表。
    图 6

  • FrmMain 的代码隐藏文件中,添加对 IRefactor.Common namespace 的引用,并将 CoursesDS 更改为完全限定名称。
    // add reference
    using IRefactor.Common;
    
    // ...
    
    private void coursesBindingNavigatorSaveItem_Click
    {
    (object sender, EventArgs e)
      // ...
      // change the coursesDS to IRefactor.Common.CoursesDS
      IRefactor.Common.CoursesDS.CoursesDataTable changes =
            this.coursesDS.Courses.GetChanges()
                as IRefactor.Common.CoursesDS.CoursesDataTable;
      // ...
    }
  • 编译解决方案并执行单元测试。
  • 遍历 IRefactor.CoursesView,移除所有先前领域对象 IRefactor.CoursesView.CoursesDS DataSet 的实例。此外,从项目中删除 CoursesDS.xsd 文件。
  • 编译解决方案并执行单元测试。

我们已成功完成第一步。我们已将名为 IRefactor.CoursesView 的 UI 项目与名为 IRefactor.Common 的领域项目分离开来。现在是时候认真对待,继续向 MVP 模式重构了。

© . All rights reserved.