UML软件工程组织

通过模式、模型、框架和工具装配应用程序
来源:www.microsoft.com 作者:Jack Greenfield
摘要:本文是一系列有关软件工厂的四篇文章中的第三篇,重点讨论如何克服在上一篇文章中探讨过的慢性问题,以及如何将关键性的创新集成到条理分明的软件工厂方法学中。
本页内容
简介 简介
新的软件开发模式 新的软件开发模式
软件模板 软件模板
软件工厂 软件工厂
产品开发 产品开发
软件供应链 软件供应链
软件大规模自定义 软件大规模自定义
小结 小结

简介

本文讨论由 Microsoft 开发的、我们称之为“软件工厂”的方法学。简言之,软件工厂是一个开发环境,该环境经过配置可以支持特定类型应用程序的快速开发。尽管软件工厂实际上只是软件开发方法和实践的持续发展过程中的下一个合乎逻辑的步骤,但它们承诺通过引入工业化模式来改变软件行业的性质。

本文是一系列有关软件工厂的四篇文章中的第三篇,重点讨论软件工厂方法学。第一篇文章描述了将推动从技能向制造进行转换的力量。第二篇文章描述了妨碍这种转换的慢性问题,以及将帮助我们克服这些问题的关键性创新。最后一篇文章回答了一些有关软件工厂的常见问题。

新的软件开发模式

在上一篇文章中,我们考察了妨碍转换的慢性问题以及将帮助我们克服这些问题的关键性创新。尽管这些关键性创新中的每一个都具有重要作用,但它们中没有一个能够单独推动从技能向制造的转换。关键在于将关键性创新集成到条理分明的方法学中。这就是软件工厂的目标。在谈论软件工厂之前,我们必须先介绍软件架构和软件模板。

软件架构

软件架构是一个文档,它以有条理的方式对用于生成和维护系统的人工制品(例如,XML 文档、模型、配置文件、生成脚本、源代码文件、SQL 文件、本地化文件、部署清单和测试用例定义)进行分类和概括,并且定义它们之间的关系,以便我们可以维护它们之间的一致性。

常见的方法是使用网格,如图 1 所示。列定义了所关心的问题,而行定义了抽象级别。每个单元格都定义了一个观点或视点,由此我们可以生成软件的某个方面。例如,对于 3 层应用程序,一个单元格可能在表示层上定义一个抽象视点,第二个单元格可能在数据层上定义一个抽象视点,第三个单元格可能在数据层上定义一个具体视点(用于开发数据库架构)。在定义了网格之后,我们可以用构成特定软件产品的开发人工制品的视图来填充它。


图 1. 描述产品系列的网格

当然,网格可以用来生成一个以上的软件产品。在用特定的视图填充它之前,它定义了生成软件产品系列的成员所需的材料清单。从软件产品系列获得提示,我们可以更进一步,并向每个单元格中添加信息,以标识用来生成每个视点所需的视图的生产资产,包括 DSL、模式、框架、工具和微过程。

网格本身并不是什么创新。真正的创新在于将网格应用于产品系列,以便标识每个单元格的生产资产,并定义可用来完全或局部地自动执行模型转换、代码生成、模式应用、测试装置构建、用户界面布局、数据库架构设计以及其他很多开发任务的单元格之间和内部的映射。正如我们已经看到的那样,我们必须使用基于高保真语言(如 XML、C# 和 SQL)的一流开发人工制品,以便提供这种自动化机制。对于模型而言,这意味着使用 DSL,而不是为文档管理设计的通用建模语言。在某些情况下,视点所描述的人工制品是模型,但通常不是这种情况。它们可以是基于形式语言的任何源人工制品,例如,高级别工作流脚本、通用编程语言源代码文件、WSDL 文件或 SQL DDL 文件。

请注意,视点不仅定义了用来开发视图的语言,而且定义了对视图的要求(通常表示为约束或模式)。例如,软件架构可能包含两个使用相同的类建模 DSL 的视点。我们可能要求根据这些视点之一建模的所有类,直接或间接地从与该视点相关联的用户界面小部件框架所提供的类继承。同样,我们也可能要求根据其他视点建模的所有类在与该视点相关联的其中一个业务实体实现模式中发挥确定的作用。在实际的软件架构中,相同的语言通常由多个视点(尤其是靠近基于通用编程语言的平台的视点)使用。

图形形式的软件架构

当然,即使是极为详细的网格也是对现实的过度简化。软件架构可以更好地表示为有向图,它的节点为视点,它的边为视点之间的可计算关系(称为映射)。这使得在网格表示中不相邻的节点可以相关。同时,它放宽了由网格施加的人为约束,即视点必须适合纯粹的分类方案,并且创建了行和列。最后,并且是最为重要的一点是,它使架构能够反映出软件体系结构。因此,例如,一系列业务应用程序的架构可能包含多个视点簇,每个簇对应于一个子系统(例如,客户管理、目录管理或订单执行)。然后,可以将每个簇中的视点进一步划分为多个子集,以反映每个子系统的分层体系结构,如图 2 所示。

软件架构描述构成软件产品的人工制品,就像 XML 架构描述构成文档的元素和属性,以及数据库架构描述构成数据库的行和列一样。像体系结构说明标准 (ADS) 一样,软件架构是用于描述软件产品系列成员的模板。但是,尽管存在这种相似性,但在软件架构和 ADS 之间还存在几个重大差异:

ADS 仅处理体系结构,而软件架构则处理软件产品系列的很多其他方面,例如,要求、可执行文件、源代码、测试装置和部署人工制品。

ADS 对设计文档进行组织,而软件架构则对开发人工制品进行组织。

尽管 ADS 蕴含一个软件产品系列,但它并不显式标识这样一个系列,也不包含用于支持基于系列的开发的机制,例如,用于表达该系列的成员与系列原型有哪些差异的方式。另一方面,软件架构面向特定的软件产品系列,并且可以实例化和自定义,以便根据特定的系列成员与系列原型之间的差异来描述该成员。

ADS 不一定支持自动化,而软件架构可以由软件模板实现,以自动执行软件开发任务(正如我们稍后将看到的那样)。

当然,软件架构的基本特性在于,它基于被组织的人工制品的各种方面(例如,它们的抽象级别、在体系结构内部的位置、功能或操作质量)对人们所关心的内容提供了多维分隔。按照 Coplien 的说法:

我们可以使用共同性和变异性原理来分析应用程序域,以便将其划分为多个子域,其中每个子域都可能适合于按照特定的范型进行设计

现在,我们可以将网格视为图形的二维投影,它在横轴上绘制了一个或多个特征,在纵轴上绘制了不同的抽象级别。另一个二维投影是特征平面,它将相关的视点投影到产品体系结构的某个部分,从而提供了从该视点观察到的统一视图。特征平面的示例包括逻辑数据、安全策略和事务平面。

软件架构实质上定义了用于生成软件产品系列成员的方法。显然,视点描述了用来准备它们的成分和工具,但是在哪里描述了准备它们的过程呢?请回想一下,过程框架是通过以下操作构建的:将微过程附加到每个视点以描述符合视图的开发,并定义约束(例如,在生成视图之前必须满足的前提条件,以及在生成视图之后必须满足的后验条件)以及在视图已经稳定时必须保持的不变体。该框架根据给定项目的需要和环境,定义了可以出现的可能过程的空间。显然,在过程框架和软件架构之间存在相似性。软件架构的视点已经定义了用于产生它们所描述的人工制品的微过程。向软件架构中添加约束以控制执行顺序也会使其成为过程框架。现在,我们具有了用于产品系列成员的方法。它定义了用来准备它们的成分和工具,以及准备它们的过程。


图 2. 软件架构

自定义软件架构

使用软件架构的一个重要方面是自定义。原始软件架构是用于生成软件产品系列的方法。但是,对于任何给定项目,我们当然只关心一个软件产品。因此,我们必须自定义软件架构,以创建用于生成该产品系列的这一特定成员的方法。这类似于厨师在使用食谱之前,考虑特殊的要求(例如,要制作的食物的份数、实际的配料或可用的工具,或者在供应膳食之前将花费的时间)以便对其进行修改时所做的工作。

软件架构自定义实际上非常简单。为了支持自定义,软件架构必须包含固定部分和可变部分。固定部分对于该产品系列的所有成员保持不变,而可变部分将更改以适应每个特定成员的独特要求。例如,联机订单输入系统的软件架构可能包含对用来生成个性化子系统的人工制品和工具进行描述的视点。但是,对于该系列的某些成员而言,个性化子系统可能不是必需的。在我们开始构建这样的系统之前,我们将首先移除与个性化有关的视点,并相应地配置我们的项目结构和工具。在不同时间,可以根据项目的需要执行自定义的不同部分。某些部分(例如,添加或丢弃全部视点以及它们之间的关系)可以基于重大的要求变更(例如,丢弃个性化)预先执行。其他部分(例如,修改视点以及它们之间的关系)可以基于更细粒度的要求变更(例如,决定使用哪些机制来驱动用户界面),随着项目的进展而逐渐执行。

软件模板

现在,所有这一切都很吸引人,但此时还都是纸上谈兵。如果我们所拥有的全部东西只有软件架构,则我们可以描述用来生成系列成员的资产,但是我们并不实际拥有这些资产。在我们可以生成任何系列成员之前,我们必须实现该软件架构,定义它所描述的 DSL、模式、框架和工具,将它们打包,然后将它们提供给产品开发人员。这些资产共同构成了软件模板。

软件模板包括可以加载到可扩展工具(例如,交互式开发环境 (IDE) 或企业生命周期工具套件)中的代码和元数据,以便自动执行系列成员的开发和维护。我们之所以将其称为软件模板,是因为它将工具配置为产生特定类型的软件,就像加载到诸如 Microsoft Word 或 Excel 之类的工具中的文档模板以将其配置为产生特定类型的文档一样。

像软件架构一样,软件模板必须针对特定的系列成员进行自定义。自定义软件架构会自定义系列成员的软件工厂的说明,但是,自定义模板则会自定义用来生成系列成员的资产。软件模板通常通过添加、丢弃或修改与那些视点相关联的资产,与软件同时进行自定义。

软件模板自定义的示例包括为要开发的子系统和组件创建项目、用要应用的模式填充选项板、设置对要使用的库的引用以及配置生成。例如,为联机商业应用程序启用内容个性化可能会导致发生下列事情:

用来配置个性化的视点被添加到架构中。

个性化配置工具出现在 IDE 菜单中。

个性化子系统的文件夹被添加到应用程序的项目中。

个性化框架和模式被导入到项目中。

Front Controller 模式(而不是 Page Controller 模式)自动应用于用户交互模型和 Web 前端设计模型之间的转换中,并且出现在 Web 前端设计器中,以便应用程序将针对不同的用户导向不同的页,而不是对所有用户显示相同的内容。

表示层的项目文件夹上的策略被修改,以便我们无法创建派生自 PageController 的类。

软件工厂

现在,我们可以将软件工厂定义为:

一个使用基于软件架构的软件模板来配置可扩展的工具,从而为产品系列提供生产设备的软件产品系列。

软件工厂的示意图如图 3 所示。


图 3. 软件工厂

产品开发

当然,生成软件工厂的目标是快速开发产品系列的成员。使用软件工厂生成产品涉及到下列活动:

问题分析。这将确定该产品是否位于软件工厂的范围之中。根据适合情况,我们可能选择在软件工厂的外部生成该产品的部分或全部。我们还可以选择生成不能很好地适合软件工厂的产品,并且在某些情况下,这可能需要更改软件架构和模板,以便它们更好地容纳那些不能很好地适应将来产品的部分。

产品规范。这会根据与产品系列要求之间的差异来定义产品要求。可以使用一系列的产品规范机制,具体取决于要求方面的差异范围,包括属性表、向导、功能模型、可视模型和结构化散文。

产品设计。这会将要求方面的差异映射到产品系列体系结构和产品开发过程方面的差异,从而生成产品体系结构和自定义的产品开发过程。

产品实现。这涉及到您熟悉的活动,例如,组件和单元测试开发、生成、单元测试执行和组件装配。可以使用一系列机制来开发实现,具体取决于差异的范围,例如,属性表、配置组件的向导和功能模型、装配组件并生成其他人工制品(例如,模型、代码和配置文件)的可视模型,以及完成框架扩展点或者创建、修改、扩展或改装组件的源代码。

产品部署。这涉及到通过供应设备、验证主机配置来创建或重用默认的部署约束、逻辑主机配置以及可执行文件到逻辑主机的映射,通过安装和配置必需的资源来重新配置主机,以及安装和配置所部署的可执行文件。

产品测试。这涉及到创建或重用测试资产(包括测试用例、测试装置、测试数据集和测试脚本)以及应用仪表化和度量工具。

产品的生产资产(包括要求、开发过程、体系结构、组件、部署配置和测试)是通过自定义软件架构定义的视点指定、重用、管理和组织的。

可变性机制

可以将一系列的机制用于产品规范和实现,具体取决于所需变更的范围。这使我们可以从构成当今软件开发大半工作的完全开放式手动编码迁移到更受约束的活动。Czarnecki 和 Eisenecker 描述了一系列可变性机制,涵盖了从日常配置到基于功能模型的分段配置、再到使用模型进行的创造性构建的广阔范围,如图 4 所示。手动编码(未显示)应该出现在这一系列机制中的模型的右侧。模式(也未显示)应该出现在模型和手动编码之间。


图 4. 可变性机制

开发过程

产品开发是由在开发软件工厂时定义的开发过程进行组织和约束的。该过程是在自定义特定系列成员的软件架构期间自定义的。

由于软件架构是过程框架,因此未指定工作流。我们可以根据项目、环境和出现的情况的需要,以我们选择的任何方式工作,前提是要满足软件架构中定义的过程约束。

我们可以按照自顶向下(即从要求到可执行文件)的方式来工作,并且使相关的人工制品保持同步。例如,我们可以开发业务实体模型,使用它来生成逻辑数据模型,然后使用该模型来生成优化的数据库架构。

我们可以利用由视点之间的横向映射定义的约束。例如,我们可以使用有关部署环境的信息(例如,可用协议)来约束服务交互的设计,以确保这些服务的实现能够正确地部署到执行环境中。

我们可以按照自底向上的方式来工作,首先为产品的各种部分开发测试装置,并且在开发组件的过程中对它们进行测试。

当软件架构被完全填充后,该过程就完成了。请注意,大部分通常为自底向上开发的工作都发生在软件工厂开发期间,此时将创建语言、模式、框架和组件,以便为产品开发提供抽象。但是,特定数量的自底向上工作总是发生在产品开发过程中,因为每个产品都具有可能无法由预先存在的抽象满足的独特要求。在充分填充自定义软件架构(这意味着我们已经为有效的视点子集开发了人工制品)之后,我们就应当能够快速地修改该实现,以适应不断更改的要求。这一快速反馈使我们可以扩展到重要的复杂性级别,同时不会丧失机动性。

软件供应链

在成熟的软件工厂中,应用程序开发主要包括组件选择、自定义、改装、扩展和装配。应用程序开发人员可以从现有组件中获得大多数必需的功能,而不是编写大量新代码。由 Clements 和 Northrop、Bosch、Weiss 和 Lai 以及其他人记录的案例研究表明,每个产品只有不到三分之一的部分是从头进行开发的,在某些情况下,甚至可以维持更高水平的重用。

但是,遍观行业现状,我们只看到少数几个基于系统化系列的产品开发案例,其中最引人注意的是打包的企业应用程序。为什么我们没有看到更多的案例?一个原因可能是缺乏认识。软件行业没有很好地理解产品系列准则。另一个原因可能是惰性。既定的准则通常难以替换,尤其是在不仅需要进行技术方面的更改,而且需要进行业务和组织方面的更改时。但是,也许最重要的原因是开发可重用资产(特别是工具)的高成本。降低成本的关键之一是避免犯下试图在内部生成所有东西的错误。

随着产品开发人员越来越多地依赖于外部供应商,供应链会逐渐形成,就像在其他行业中一样。按照 Lee 和 Billington 的说法,供应链是一个网络,它以原始材料为起点,将它们转换为中间产品,然后转换为最终的产品,以便通过配送系统提供给客户。每个参与者都消耗来自一个或多个上游供应商的逻辑和/或物理产品,通常通过应用它们来开发更复杂的产品来使其增值,然后将成果提供给下游消费者。

换句话说,供应商互相连接在一起,以便使来自上游供应商的输出变成下游供应商的输入。上游供应商可以向下游供应商提供实现资产(例如,组件)或过程资产(例如,工具和过程文档)。在供应链中这两种类型的资产之间,存在着重要的差异。由于下游供应商生产的产品要结合上游供应商提供的实现资产,因此下游供应商具有较窄的活动范围,并且生产的产品比上游产品更大。例如,消耗用户界面框架的联机商业系统的供应商就具有较窄的活动中心,并且生产的产品比用户界面框架的供应商所生产的更大。这一点对于过程资产可能不成立。例如,用户界面框架供应商可能会消耗既比它所提供的用户界面框架更大、范围更窄的配置管理和缺陷跟踪工具。

供应链所开发的产品跨越了多个组织,并且将消费者与供应商进行分隔。常用的组件被商品化,并且出现了按订货条款进行制造的从事多种领域生产的供应商,从而使得单个供应商生产起来过于昂贵的产品能够容易地获得。需求成为客户关系管理的焦点。服务级别协议控制着消费者和供应商之间的交易。维修和援助按照保修单提供。

软件供应链获得成功的关键之一是使供应商的体系结构和过程相互匹配。正如我们已经看到的那样,组件合成的主要障碍之一是体系结构不匹配。如果组件不是通过有关它们相互之间的交互方式以及它们与执行它们的平台之间的交互方式的兼容假设设计的,则未经广泛的修改、改装或调解,它们将无法装配。这些假设的兼容性越低,体系结构不匹配的水平就越高,并且装配组件所需的改装量就越大。如果体系结构的不匹配达到某种水平,则可能无法成功地装配组件。类似的匹配问题适用于寻求使用或提供过程资产的供应商的开发过程。例如,使用来自一个供应商的测试工具的供应商可能无法使用来自另一个供应商的缺陷跟踪工具 — 如果这些工具无法与测试工具集成的话。集成可能需要使用就开发过程进行兼容假设的工具。

要确保必需的匹配,一种方法是将约束显式包括为组件包中的元数据,使它们能够向下游传播。这种约束传播的示例已经存在于开发工具和部署工具之间,其中,开发工具将对目标平台配置的约束作为元数据放置在可部署的组件包中。然后,部署工具将收集这些约束,并且根据需要配置目标平台以满足这些约束,或者验证目标平台是否正确配置,如果违反了这些约束,则禁止部署。

约束传播会影响要求、体系结构和实现。

下游供应商会从上游供应商那里继承固定的和可变的要求。然后,它们将聚集一些可变的要求,并为更下游的供应商将它们转变为固定的要求。例如,联机商业系统供应商可能会从用户界面框架供应商那里继承固定的用户界面要求,例如,所有联机商业系统都需要基于 Web 的要求。

下游供应商会继承或聚合它们的供应商体系结构的某些部分。例如,联机商业系统供应商会从用户界面框架供应商那里继承其体系结构的用户界面部分。

下游供应商可能会改装或聚合来自它们的供应商的组件。例如,联机商业系统供应商可能会通过用于浏览产品目录的自定义控件来扩展用户界面框架。

在汽车和电信行业的嵌入式软件开发中,可以找到软件供应链的一些最高级的示例。上面描述的匹配问题已经成为这些先驱者的重大难题。例如,作为系统集成商的 Daimler Chrysler 规定了他们在其汽车产品中放置的系统的总体体系结构,同时又依赖代理供应商(如 Bosch 和 Siemens)来提供大部分软件组件。管理系统集成商和组件供应商之间的关系可能非常困难。这些困难包括:指定代理组件而不查看它们的源代码,同时使迭代和修改的数量保持较低水平以便管理成本,决定将哪些内容保留在内部以及将哪些内容提供给外部,以及保护参与各方(其中的一些参与方可能是竞争对手,而不像在其他环境中那样是合作伙伴)的知识产权。

因为存在这些关系,所以供应链造成了下面这些进行标准化的压力:

产品规范格式,以便简化消费者和供应商之间的协商。

常见领域的资产,特别是体系结构和模式,以便能够更加容易地跨平台装配独立开发的组件,并获得可预测的结果。

打包包含组件元数据在内的格式,使组件更加容易发现、选择、许可、配置、安装、改装、装配、部署、监视和管理。

您可能会说,虽然很好,但这些种类的关系已经存在了。尽管确实是这样,但软件行业中互相依赖的水平要比更成熟的行业低得多。但是,这是一个好消息,因为这意味着我们有机会显著降低成本,减少将产品投放市场所需的时间,并且通过提高互相依赖的水平以及吸取其他行业中有关如何使供应链变得更有效的经验来提高软件产品的质量。遗憾的是,对这一机会的详细探讨超出了本文的范围。

供应链是如何形成的

软件工厂通过划分软件架构(纵向或横向)以便将职责转移给外部供应商来促进供应链的形成。

纵向划分使软件工厂可以装配由上游供应商提供的组件。例如,请设想一下上述示例中的实体框架来自独立软件供应商 (ISV)。

横向划分可以分隔产品系列和产品开发人员,使得产品开发人员可以使用由处于供应链同一级别的外部产品系列开发人员提供的生产资产。这可以采取下列两种形式之一:

产品系列开发被外包。例如,请设想一下,开发软件工厂的产品系列开发人员为外部的系统集成商 (SI) 工作,而不是为上述示例中的 ISV 工作。他们为客户组织中的开发人员生成软件工厂,而不是为内部开发人员生成它们。

产品开发被外包。例如,请设想一下,上述示例中的产品开发人员为 SI 工作,并使用为 ISV 工作的产品系列开发人员所开发的软件工厂。产品开发人员可能位于境外成本较低的劳务市场中。

当然,这两种类型的划分可以出现在架构的任何位置,并且它们可以按任意方式进行组合。它们还可以随着业务状况的变化而消失和重新出现。在成熟的行业中,有很多个级别的供应商为最终的成品做出贡献,并且随着供应商进入或离开关系,供应链会不断发生动荡。

软件大规模自定义

某些行业(例如,基于 Web 的 PC 业务)现在可以根据需要为单个客户廉价而快速地生产产品变体。尽管软件产品的大规模自定义在一段时间内还不会出现,但是软件工厂的广泛采用将使其成为可能。目前,在其他行业中,当企业优化它们的内部价值链时,这种现象就会发生。按照 Porter 的说法,价值链是单个组织内部的一系列增值活动,这些活动将它的供应端与它的需求端联系起来。价值链优化需要将诸如客户关系管理、需求管理、产品定义、产品设计、产品装配和供应链管理之类的业务过程集成在一起,如图 5 所示。当软件供应商优化它们的价值链时,就会出现软件产品的大规模自定义。请设想一下,按照目前订购自定义 PC 的相同方式从 Web 站点订购自定义的财务服务应用程序,同时获得相同级别的置信度和具有可比性的交货时间。


图 5. 价值链集成

小结

软件工厂建立在对系统化重用、模型驱动开发、装配式开发和过程框架中的关键思想进行融合的基础之上。其中的许多思想都没有什么新颖之处。真正的新颖之处在于将它们综合成一种集成式方法,以便使具有领域专业知识的组织能够实现软件工厂模式,并且生成语言、模式、框架和工具以自动执行狭窄领域中的开发。

我们认为,软件工厂远景的关键部分将会迅速实现,并且某些部分只需要若干年。能够承载软件工厂的商业工具现在就可以获得,其中包括 Microsoft Visual Studio .NET 和 IBM WebSphere Studio。DSL 技术比大多数其他技术都要新颖得多,并且它依赖于可扩展语言系列。但是,DSL 开发工具和框架目前正在开发中,并且已经开始以商业形式出现。

本文描述了一种称为软件工厂的方法学,它能够集成关键性的创新,以促进从技能向制造的转换。本系列中的下一篇文章将回答有关软件工厂的常见问题。

有很多问题只在本文中进行了简短讨论,从而为读者留有更大空间以获得证明或更详细的讨论。该主题在 Software Factories:Assembling Applications with Patterns, Models, Frameworks and Tools 一书(作者为 John Wiley and Sons 的 Jack Greenfield 和 Keith Short)中进行了更详细的讨论。

 

版权所有:UML软件工程组织