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

设计 Web 应用程序的新方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (30投票s)

2006 年 2 月 10 日

15分钟阅读

viewsIcon

137761

我们需要一种新的方式来构建我们的应用程序。与其在开发过程中分散人工工作和需要决策的项目,我们不如先做“思考”的事情,然后再自动化其余部分。为什么我们不停止用困难的方式做事呢?

Data First Development application tier model

图 1:具有隔离生成代码的多层应用程序

引言

UML 建模被炒得沸沸扬扬,你会认为那是设计应用程序的唯一方法。我要告诉你一个消息。那是错误的方法。

现在,在你因为我的异端邪说而抨击我之前,请阅读本段的其余部分。就像人类和果蝇基因之间有 60% 的基因相似度一样,几乎所有的 Web 应用程序都是相同的。只不过我会把这种共同性设定在接近 75%-90%。我挑战任何开发过两个以上 Web 应用程序的人来反驳这个事实。面对现实吧,几乎所有应用程序所做的就是获取数据,对其进行处理,然后将其放回。甚至还有这些应用程序的缩写:CRUD(创建、检索、更新、删除)。这当然会引出显而易见的俏皮话:“我这辈子见过很多 CRUD 的应用程序!”或者“那个应用程序就是 CRUD!”

一种新的构建方式

许多公司目前的“最佳实践”是构建包含参与者、用例和不同关系映射的大型类图,然后根据这些图和文档实现代码。DBA 需要构建一个数据库来支持这个理论框架。同时,开发人员开始构建与图匹配的对象。理论上,更改需要变更单、模型重新设计,以及彻底废弃代码进行重写。

Traditional UML/Documentation driven application development

图 2:传统 SDLC 设计-构建流程

在实际软件构建的三天内,这些图表与应用程序就会严重脱节。再过两周,这些图表就会在某个文档库中腐烂,再也不会被看到或听到。

旁注:UML 并不像我所描述的那么糟糕。它在理解复杂问题时非常有用,但对于简单的问题领域往往被过度使用,实际上可能导致软件质量下降。但是文档是另一篇文章的主题。

我们需要一种新的方式来构建我们的应用程序。与其在开发过程中分散人工工作和需要决策的项目,我们不如先做“思考”的事情,然后再自动化其余部分。为什么我们不停止用困难的方式做事呢?为什么不直接构建我们的数据库,然后运行代码生成器来生成基本的实体对象和数据访问层呢?

进入数据优先开发(我看看我能不能在这里创造这个词)。

数据优先开发 (DFD)

Traditional UML/Documentation driven application development

图 3:数据优先开发

数据优先开发(DFD)围绕着自下而上地构建应用程序展开。它是一种最适合小型团队或个人开发人员进行 N 层开发的方法,但也可以应用于大型团队。就像建造房屋从地基开始一样,DFD 要求您从应用程序的基础(数据库)开始。房屋从地基开始,然后是墙壁、屋顶,最后是所有的装修工作和可冲水的马桶。DFD 将数据库用作基础,然后构建对象模型结构(墙壁和屋顶),最后是 UI(固定装置)。不过,如果您的房屋墙壁建在脚轮或轨道上,可以安全地移动和重新配置房间,我们的比喻会更恰当。

DFD 并非一个完整的应用程序生命周期开发方法论。它是一种将现有工具和技术缝合在一起的实用技术,用于快速高效地构建特定类别的应用程序:多层、分布式和数据支持的应用程序——其中最常见的就是数据驱动的 Web 应用程序。

在数据优先开发中,数据库首先被设计和构建,并且速度很快。与其等待两个月才能完成 95% 的数据模型,不如构建并开始开发一个 80% 完成度的数据模型。无论你有多优秀,无论你的需求有多可靠,你的数据需求都会在开发过程中发生变化和演进。这些变化需要尽快暴露出来,而开发可工作的代码是暴露这些变化的最短路径。但不要害怕——你生成的代码将使你免于早期的失误。

DFD 步骤

  1. 构建您的数据库
  2. 生成您的实体/工厂代码(可能还有 UI 框架)
  3. 根据需要进行子类化和扩展
  4. ...根据设计需求的变化迭代并重复步骤 1-3
  5. 生成项目文档

为了完全有效,这项技术要求您将生成的代码尽可能与应用程序的其余代码隔离。这包括设计松耦合的前端。这种方法论的一个重要原则是隔离您的实体/数据访问层,以便在底层数据发生变化时可以将其替换掉,而不会影响您的主要业务逻辑或前端设计和代码交互(参见上面的图 1)。在最好的情况下,您将生成的代码视为一个黑盒,可以随意替换。

DFD 有何新颖之处?

现在,我知道所有真正的艺术学徒都在准备争论这只是MDA(模型驱动架构)和/或 ORM(对象角色建模对象关系映射,取决于你问谁),只是用了不同的术语。嗯,也许,某种程度上,以及不是(不一定是这个顺序)。

MDA 围绕着以与实现无关的格式(如 UML,这正是我们试图摆脱的)维护软件的核心模型。DFD 可以利用中立的建模格式,但更可能的是,您将从某个系统方面(数据库)的特定实现开始,然后从该实现生成您的工件(代码、测试和文档)。以前的高端代码生成器,如 Rational Rose(现由 IBM 拥有),旨在从 UML 进行建模和构建。我们使用的许多新出现的代码生成工具(开源和商业)直接从数据库构建代码。

对象关系映射是将现有对象的数据字段映射到持久化存储的过程。这假设一个现有的代码对象必须向下映射到现有的数据库。这与 DFD 完全相反,DFD 是仅从数据库向上生成对象代码。另一方面,对象角色建模是一种对复杂相互关系和业务逻辑进行建模的方法。这比 DFD 实现的抽象层更高,并且具有超越 DFD 能力的更广泛的应用。

选择合适的代码生成器

您必须使用代码生成器,既为了质量原因,也为了防止腕管综合症。下面,我将概述一些可用选项以及选择时需要考虑的一些因素。Rajesh Sadashivan 也有一篇关于当前可用资源和生成器的非常好的文章:ORM、代码生成和一些关于 MDA 的知识

我们将在决策部分讨论的主题

  • 存储过程支持
  • 项目定义和工具脚本化
  • 可扩展性和模板
  • 语言支持
  • ORM vs. 硬编码
  • 完整应用程序存根支持
  • 单元测试

构建还是购买您的生成器

软件领域永恒的问题。早在 2000 年初,当我开始涉足这个领域时,答案很简单:自己构建。

我在 2000 年初为 VB6 编写了我的第一个实体/工厂代码生成器。从那时起,我为 VB6、Java、J2EE、C# 和 PHP 构建了几个版本。我甚至用 CodeDOM 和 C# 写了一系列关于这个主题的文章。我目前维护和增强着几个应用程序,所以我保留了这些各种工具,无论是好是坏。

如今,答案有所不同。在过去几年里,用于对象映射和代码生成(或完整应用程序生成)的工具爆炸式增长。每个人似乎几乎同时意识到:构建 N 层应用程序在很大程度上是一项重复性任务,一个受过训练的猴子都能完成,更适合代码生成器。如今,随着代码生成工具的普及,只有傻瓜或控制狂才会编写和维护自己的工具包。唉,我就是那个傻瓜。但我跑题了。

抉择,抉择,抉择

无论购买还是构建,您都将面临相同的决策。在“购买”的情况下,您正在评估现有产品的功能集。在“构建”的情况下,您正在设计工具的功能。在您决定使用某个工具之前,回答这些问题非常重要。

存储过程支持

这对你有多重要?存储过程通过其参数化性质有助于防止 SQL 注入攻击,并且比内联 SQL 语句快得多(我听说快 10 倍以上)。它们也可能更难以维护,将部分业务逻辑暴露给客户端(尽管在大多数情况下微不足道),并且并非所有数据库都完全支持它们。

项目定义和工具脚本化

使用点选界面进行一次性构建是可以的。对于多次构建,您会希望有一个定义的“项目”来封装所有目标表/类和任何构建选项。对于真正精简的开发环境,您会希望有一个可以(天哪)命令行驱动的工具。如果您坚持 DFD 原则,即利用隔离且完全生成的黑盒实体对象和数据访问层,您会希望将这种重新生成自动化作为持续集成过程的一部分(可能与 CruiseControlAnt/Nant 结合使用)。您正在进行持续集成,不是吗?

可扩展性和模板

将来需要修改输出代码吗?不管你喜不喜欢,答案很可能是肯定的。语言会发展并实现更好的功能。会发现错误。需要实现优化。虽然一个产品可能能够容忍生成代码的静态设计,但在几代和几个产品之后,你将希望底层生成代码的风格发生变化。

专有工具通常会生成最干净的代码,但你会被它们束缚,并依赖它们来更新工具。开源允许修改,但代码定制的成本很高;自制系统也是如此。我的建议是找到一个好的基于模板的解决方案,并围绕它建立一个活跃的社区,然后学习该工具。

语言支持

您总是使用<插入语言> [C#|Java|Ruby|VB|PHP] 进行构建,还是使用不同的语言构建不同的产品?大多数商业产品将支持多种语言,但仅限于一个家族内(例如:C#、VB 和 JScript)。有一些工具同时支持 Java 和 .NET 语言家族。

基于模板的解决方案往往具有更广泛的语言支持,以及更好的可扩展性。MyGeneration 似乎拥有最广泛的语言支持和一个活跃的开发社区,不断添加新的模板和语言支持。

如果您自己构建工具并使用 .NET,一个选项是使用 .NET CodeDOM 类。理论上,这意味着任何语言都可以作为输出,只要编写了适当的 CodeDOM CodeProvider。实际上,这意味着您只能输出 VB 和 C# 代码(也许还有 J#,但除非您想要一个 zip 库,否则没人会用它)。使用不同语言的全部意义在于利用特定语言的功能。无论抽象级别如何,有些语言就是以不同的方式做事,因此代码生成抽象层永远无法超越给定的语言家族(例如:.NET 语言)。我发现 CodeDOM 丑陋且难以实现,并且我的生成器增加了 500% 的代码膨胀。

对象关系映射 vs. 硬编码

基于映射的框架允许在运行时将数据库列映射到对象属性,通常通过 XML 映射文件。这些工具方便灵活。它们并不总是带有生成器,但编写一个输出实体类和 XML 映射文件的工具应该很简单。话虽如此,我建议不要使用任何涉及运行时映射的方法。它将是后期绑定的,并因此遭受严重的性能损失。最好使用提供硬编码和早期绑定的工具。灵活性不值得付出代价。

基于映射的框架

完整应用程序存根开发

一些更复杂的工具可以远远超出生成对象数据访问层。其中一些还可以构建功能齐全的用户界面。如果您倾向于做非常直接的 CRUD 应用程序,这可以节省大量时间。一个完全生成的应用程序对于许多公司 IT 部门被要求开发的内部项目来说是完全足够的。我通常将这些工具生成的 UI 视为最好的方便起点,并且只会生成一次前端代码,然后从那里进行手动修改。

单元测试

MyGeneration 是我所知的唯一一个甚至涉及单元测试自动生成的工具。不要否认单元测试的重要性。感受单元测试的力量!你不能指望控制单元测试,你只能指望限制它们!你的基本测试没有理由不能自动生成,尽管更实质性的测试将是手工编码的,特别是如果你正在做 TDD(测试驱动开发)

您遇到的大多数单元测试代码都将针对 NUnit,但我认为 CSUnit 更好,因为它集成了 Visual Studio。由于 VS 2005 内置了单元测试框架,这可能都是一个无关紧要的问题。

回填文档

我将把关于文档的完整讨论留到另一篇文章(正在撰写中),但我确实想提一下。

传统的 SDLC 要求所有代码在实现之前进行设计和文档化。任何参与过完整生命周期发布的人都会告诉你,一旦团队开始编写代码,这个模型就会被抛弃。在严格的 SDLC 流程环境中,团队花费几乎与编写代码一样多的时间更新规范的情况并不少见。而且两者从未同步。

就像我们的代码对象模型是从数据结构构建起来的一样,大部分文档也可以从代码中构建起来。俗话说,事实胜于雄辩,从功能代码生成的文档更不容易撒谎。

Java 阵营是第一个(据我所知)将从代码构建文档(即 Javadoc)的过程正式化的。微软也以自己的方式效仿,但内置工具仍有不足之处。您将需要某种附加工具来完成这项工作。还有一些不错的 UML 图逆向工程工具,但许多最好的工具仅适用于 Java 阵营。

摘要

天哪,这文章太啰嗦了!为了简洁,我将用几个简单的步骤概述我实际/理想的进行数据优先开发(DFD)的工具和方法。

逐步操作

这描述了使用 C# 进行代码开发和 MS SQL Server 作为后端的 MS 堆栈应用程序的构建,但您可以根据需要进行推断。任何带有[理想]的都意味着我正在管理一个开发团队和/或其他人正在为我的工具付费。

第 0 步:对您要构建什么有一些想法

在这个过程之外,我从足够的产品需求开始系统实施。如果您想了解我获取和管理产品需求的最佳技术,请查看 Scrum

    工具

  • 一个有产品愿景和/或有钱烧得慌的人[理想]

第 1 步:构建数据库

仅限于数据表和关系。

    工具

  • Microsoft SQL Server Enterprise Manager(表构建 GUI 和图表工具)。
  • ER/Win [理想]。(谁知道 CA 收购了它们?产品可能在 3 年内就会变得很糟糕。)

第 2 步:生成对象模型代码

使用选定的代码生成工具构建实体和工厂类,以及存储过程和初步单元测试。将最终产品代码(实体/工厂类)打包在一个单独的 DLL 中。

第 3 步:构建自定义业务逻辑

每当您想修改生成的代码时,就对其进行子类化。这可以使您免受在需要重新生成内容时自定义代码被覆盖的困扰。我实际上喜欢将核心代码重新生成保持在我的自动构建/持续集成过程中。此过程的一部分还在于构建和运行合理数量的单元测试。

    工具

  • 任何合适的 IDE(在本例中为 Visual Studio)。
  • 当我在墨西哥度假时,薪水过低的计算机科学实习生[理想]
  • CruiseControl.Net
  • Nant
  • CSUnit

第 4 步:生成项目文档

早期阶段的文档有助于定义应用程序的范围和需求,但很少有其他用途。在开发过程中维护正式文档是一种负担,可能会延迟产品发布。如果需要,我会在项目完成或准备发布后生成文档(因为一件艺术品何时真正完成?)。这基本上与我在学校写学期论文提纲时采取的方法相同。

    工具

  • NDoc
  • (再一次)当我在墨西哥度假时,薪水过低的计算机科学实习生[理想]

在大多数情况下,步骤 1(数据库构建)和步骤 2(代码生成)将在步骤 3(自定义编码)期间循环。这种方法的全部目的是减少重复编码,并保护产品和您自己免受开发中期需求变更的影响。

结论

多年来,业界对构建多层分布式应用程序的理解不断演进。这里概述的技术是进化的,而非革命性的。遵循“从数据库构建”方法的代码生成器爆炸式增长,证明了这项技术的有效性和普及程度。

随着我们简化那些繁琐的任务,将有更多的时间和精力投入到创新产品的创造上,而不是在管道代码上。数据优先开发 (DFD) 是一种试图将所有奇妙的新代码生成器正式化为流程的技术,以简化开发并提高交付 N 层应用程序的整体质量和速度。

即使您继续进行模型和文档驱动的开发,也请认真考虑使用代码生成器。您的整体产品质量将大大提高。

© . All rights reserved.