第7章构造阶段 在设计完成后,构造阶段是根据设计模型“生产“软件系统的过程。在这个阶 段,关注的重点是如何高效、高质量地把软件代码编写完成。我们需要确定开发 环境、制定编码规范,并按照计划协调各个团队开展工作。 尽管构造阶段看上去似乎不像设计阶段没有那么多创造性,设计中依然也包 括了一些决策和创新工作。有一些人认为如果设计模型做得足够详细,那么代码 生成就是一个机械的翻译过程。这一观点并不正确,因为在构造阶段,必然会发 现一些在设计阶段没有考虑到的细节,或者在设计阶段考虑不够正确的情况,这 些问题需要一一解决。 在本章中,我们将介绍与构造相关的一些概念,包括正向工程、逆向工程、 重构、单元测试等概念。然后介绍依据模型生成代码的具体方法,并对设计中可 能进行的优化进行具体的讨论。 7.1构造阶段的主要内容 构造阶段要完成代码的编写、数据库的设计等工作。构造阶段中运用建模工 具、编程环境、测试工具等多种有助于提高生产力的手段。构造阶段也需要按照 一定的规范去编写代码,以形成风格一致、良好的代码。 在构造阶段,我们除了提交完成的代码、数据库定义文件等交付物,还将撰 写模块开发卷宗,以对自己的开发工作进行记录。 7.2正向工程与逆向工程 7.2.1正向工程与模型驱动的体系架构 正向工程(forward engineering)是通过到实现语言的映射而把模型转换为 代码的过程。通过正向工程可以实现从建模到代码的联接和过渡。许多工具提供 了正向工程的支持
第 7 章 构造阶段 在设计完成后,构造阶段是根据设计模型“生产“软件系统的过程。在这个阶 段,关注的重点是如何高效、高质量地把软件代码编写完成。我们需要确定开发 环境、制定编码规范,并按照计划协调各个团队开展工作。 尽管构造阶段看上去似乎不像设计阶段没有那么多创造性,设计中依然也包 括了一些决策和创新工作。有一些人认为如果设计模型做得足够详细,那么代码 生成就是一个机械的翻译过程。这一观点并不正确,因为在构造阶段,必然会发 现一些在设计阶段没有考虑到的细节,或者在设计阶段考虑不够正确的情况,这 些问题需要一一解决。 在本章中,我们将介绍与构造相关的一些概念,包括正向工程、逆向工程、 重构、单元测试等概念。然后介绍依据模型生成代码的具体方法,并对设计中可 能进行的优化进行具体的讨论。 7.1 构造阶段的主要内容 构造阶段要完成代码的编写、数据库的设计等工作。构造阶段中运用建模工 具、编程环境、测试工具等多种有助于提高生产力的手段。构造阶段也需要按照 一定的规范去编写代码,以形成风格一致、良好的代码。 在构造阶段,我们除了提交完成的代码、数据库定义文件等交付物,还将撰 写模块开发卷宗,以对自己的开发工作进行记录。 7.2 正向工程与逆向工程 7.2.1 正向工程与模型驱动的体系架构 正向工程(forward engineering)是通过到实现语言的映射而把模型转换为 代码的过程。通过正向工程可以实现从建模到代码的联接和过渡。许多工具提供 了正向工程的支持
长久以来,在软件工程领域有一个梦想,那就是仅仅通过构造模型而不用编 写代码就能完成软件开发。随着许多技术包括中间件、UML、XML、软件框架、 设计模式、正向工程等的发展,我们向这一目标不断前进。 模型驱动体系架构(Model Driven Architecture,MDA)是由国际标准化组 织对象管理联盟(Object Management Group,.OMG)提出的一个构想,目的是将 目前的开发行为提升到更高的抽象层级一一分析模型级,把针对特定计算平台的 编码工作交由机器自动完成,这样的情况下,业务逻辑与实现技术实现了解耦, 二者相对独立变化,因此模型的价值被最大化。 MDA在目前技术的基础上,分离出了两个抽象级别的模型:平台无关模型 (Platform Independent Model,.PIM)和平台相关模型(Platform Specialize Model, PSM)。PIM是一个纯粹的不考虑实现技术的分析模型,而PSM可以视为一个基 于特定实现技术,比如J2EE的设计模型。工程师们只需要建立表达业务逻辑的 PIM,从PIM到PSM及至代码实现都是由第三方的自动化工具来完成的。 为了实现MDA,OMG制定了一系列的标准: ● UML:UML被MDA用来描述各种模型。它是MDA的基础。 ● MOF:MOF(Meta Object Facility)是比UML更高层次的抽象,它的目的是 为了描述UML的扩展或者其它未来可能出现的类UML的建模语言。 ● XMI:XMI(XML-based metadata Interchange)是基于XML的元数据交换。 它通过标准化的XML文档格式和DTDs(Document Type Definitions)为各 种模型定义了一种基于XML的数据交换格式。这使得作为最终产品的模型 可以在各种不同的工具中传递。 ●CWM:CWM(Common Warehouse Metamodel)提供了一种数据格式变换 的手段,在任意级别的模型上都可以使用CWM来描述两种数据模型之间的 映射规则。 UML、MOF、XMI、CWM等一系列标准分别解决了MDA的模型建立、模型 扩展、模型交换、模型变换这几个方面的问题。OMG试图通过标准化的定义, 扩大MDA的应用范围。同时通过这样一个可扩展的建模语言环境,T厂商可以 自由实现自己的建模语言,以及语言到可执行代码的映射。 针对MDA的思想,目前不少厂商已经推出了一些工具。然而,目前离OMG 的最终构想还存在一定的差距
长久以来,在软件工程领域有一个梦想,那就是仅仅通过构造模型而不用编 写代码就能完成软件开发。随着许多技术包括中间件、UML、XML、软件框架、 设计模式、正向工程等的发展,我们向这一目标不断前进。 模型驱动体系架构(Model Driven Architecture,MDA)是由国际标准化组 织对象管理联盟(Object Management Group, OMG)提出的一个构想,目的是将 目前的开发行为提升到更高的抽象层级——分析模型级,把针对特定计算平台的 编码工作交由机器自动完成,这样的情况下,业务逻辑与实现技术实现了解耦, 二者相对独立变化,因此模型的价值被最大化。 MDA 在目前技术的基础上,分离出了两个抽象级别的模型:平台无关模型 (Platform Independent Model,PIM)和平台相关模型(Platform Specialize Model, PSM)。PIM 是一个纯粹的不考虑实现技术的分析模型,而 PSM 可以视为一个基 于特定实现技术,比如 J2EE 的设计模型。工程师们只需要建立表达业务逻辑的 PIM,从 PIM 到 PSM 及至代码实现都是由第三方的自动化工具来完成的。 为了实现 MDA,OMG 制定了一系列的标准: UML:UML 被 MDA 用来描述各种模型。它是 MDA 的基础。 MOF:MOF(Meta Object Facility)是比 UML 更高层次的抽象,它的目的是 为了描述 UML 的扩展或者其它未来可能出现的类 UML 的建模语言。 XMI:XMI(XML-based metadata Interchange)是基于 XML 的元数据交换。 它通过标准化的 XML 文档格式和 DTDs(Document Type Definitions)为各 种模型定义了一种基于 XML 的数据交换格式。这使得作为最终产品的模型 可以在各种不同的工具中传递。 CWM:CWM(Common Warehouse Metamodel)提供了一种数据格式变换 的手段,在任意级别的模型上都可以使用 CWM 来描述两种数据模型之间的 映射规则。 UML、MOF、XMI、CWM 等一系列标准分别解决了 MDA 的模型建立、模型 扩展、模型交换、模型变换这几个方面的问题。OMG 试图通过标准化的定义, 扩大 MDA 的应用范围。同时通过这样一个可扩展的建模语言环境,IT 厂商可以 自由实现自己的建模语言,以及语言到可执行代码的映射。 针对 MDA 的思想,目前不少厂商已经推出了一些工具。然而,目前离 OMG 的最终构想还存在一定的差距
Finance Manutacturing E-Commerce e CORBA CES UML Space Model Driven Telecom Architecture MOF CWM JAVA .NET EVENTS Transportation HealthCare More.. 图7-1模型驱动体系架构示意图 7.2.2逆向工程 “逆向工程”的起源是对硬件产品的分析。人们通过分析竞争对手的硬件产 品以便改进自己的产品、或者仿照对手的产品。这个概念应用到软件系统时,指 的是人们利用方法获得对软件系统及软件系统结构的理解。对一个硬件系统实施 逆向工程,一般是为了得到这个系统的复制品:而对一个软件系统实施逆向工程, 通常是为了获得对这个系统在设计层次上的理解,以便于系统的维护、巩固、移 植。 在基于模型进行软件开发的过程中,我们通过正向工程生成了代码框架,在 编写代码的过程中可能会对原来的代码框架进行修改,这些修改会导致模型与代 码的不一致,因此,我们通过逆向工程再次生成模型,从而保证模型与代码的一 致性。 近几年来,开放源代码逐渐成为一种趋势,为了达到软件架构和设计模式的 复用,从源代码中获取软件设计模式和架构模式也将成为广泛的需求,逆向工程 就是一个有用的工具。 7.3单元测试与测试驱动开发 在软件开发过程中,开发者需要保证自己编写的代码是正确的。单元测试就 是由开发者自己编写一小段代码,用于检验被测代码的某一小的、明确的功能是 否正确
图 7-1 模型驱动体系架构示意图 7.2.2 逆向工程 “逆向工程”的起源是对硬件产品的分析。人们通过分析竞争对手的硬件产 品以便改进自己的产品、或者仿照对手的产品。这个概念应用到软件系统时,指 的是人们利用方法获得对软件系统及软件系统结构的理解。对一个硬件系统实施 逆向工程,一般是为了得到这个系统的复制品;而对一个软件系统实施逆向工程, 通常是为了获得对这个系统在设计层次上的理解,以便于系统的维护、巩固、移 植。 在基于模型进行软件开发的过程中,我们通过正向工程生成了代码框架,在 编写代码的过程中可能会对原来的代码框架进行修改,这些修改会导致模型与代 码的不一致,因此,我们通过逆向工程再次生成模型,从而保证模型与代码的一 致性。 近几年来,开放源代码逐渐成为一种趋势,为了达到软件架构和设计模式的 复用,从源代码中获取软件设计模式和架构模式也将成为广泛的需求,逆向工程 就是一个有用的工具。 7.3 单元测试与测试驱动开发 在软件开发过程中,开发者需要保证自己编写的代码是正确的。单元测试就 是由开发者自己编写一小段代码,用于检验被测代码的某一小的、明确的功能是 否正确
单元测试是由程序员自己来完成。程序员有责任编写功能代码,同时也就有 责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为 和我们期望的一致。 在实际软件开发过程中,有些时候程序员会由于某些原因没有进行充分的单 元测试,给软件留下了危险。为了改变这个情况,Kent Beck最早在其极限编程 (XP)方法论中,向大家推荐“测试驱动”这一最佳实践,还专门撰写了《测试驱 动开发》一书,详细说明如何实现。测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。 它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功 能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代 码,并加速开发过程。 7.4软件重构 一个项目的代码质量往往有可能会随着时间的推移变得越来糟糕,代码越来 越臃肿,越来越难以理解它的本意,添加新功能越来越难。 重构通常指在不改变代码的外部行为情况下而修改源代码。重构是代码维护 中的一部分,既不修正错误,又不增加新的功能性。而是用于提高代码的可读性 或者改变代码的结构和设计,使其在将来更容易被维护。特别是,在现有的程序 的结构下,给一个程序增加一个新的行为会非常困难,因此开发人员可能先重构 这部分代码,使加入新的行为变得容易。 Martin Fowler等人总结出了一些常用的重构技术,将其写成了一本面向对 象领域的经典著作《重构:改善既有代码的设计》。 重构有以下好处: 1.重构能够改进软件设计,可以减少代码量,使以后的维护,开发更方便 2.重构使软件更容易理解 3.重构的时候,由于必须去阅读代码,分析、理解逻辑,这样就很可能发现一 些逻辑或是简单的错误。 重构可以在添加新功能时、修改错误时、复审代码时进行。 当开发进入尾声的时候,这时候不应该重构,因为这时候重构往往得不偿失, 可能会引入错误,还会拖累项目交付时间
单元测试是由程序员自己来完成。程序员有责任编写功能代码,同时也就有 责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为 和我们期望的一致。 在实际软件开发过程中,有些时候程序员会由于某些原因没有进行充分的单 元测试,给软件留下了危险。为了改变这个情况,Kent Beck 最早在其极限编程 (XP)方法论中,向大家推荐“测试驱动”这一最佳实践,还专门撰写了《测试驱 动开发》一书,详细说明如何实现。测试驱动开发,英文全称 Test-Driven Development,简称 TDD,是一种不同于传统软件开发流程的新型的开发方法。 它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功 能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代 码,并加速开发过程。 7.4 软件重构 一个项目的代码质量往往有可能会随着时间的推移变得越来糟糕,代码越来 越臃肿,越来越难以理解它的本意,添加新功能越来越难。 重构通常指在不改变代码的外部行为情况下而修改源代码。重构是代码维护 中的一部分,既不修正错误,又不增加新的功能性。而是用于提高代码的可读性 或者改变代码的结构和设计,使其在将来更容易被维护。特别是,在现有的程序 的结构下,给一个程序增加一个新的行为会非常困难,因此开发人员可能先重构 这部分代码,使加入新的行为变得容易。 Martin Fowler 等人总结出了一些常用的重构技术,将其写成了一本面向对 象领域的经典著作《重构:改善既有代码的设计》。 重构有以下好处: 1. 重构能够改进软件设计,可以减少代码量,使以后的维护,开发更方便 2. 重构使软件更容易理解 3. 重构的时候,由于必须去阅读代码,分析、理解逻辑,这样就很可能发现一 些逻辑或是简单的错误。 重构可以在添加新功能时、修改错误时、复审代码时进行。 当开发进入尾声的时候,这时候不应该重构,因为这时候重构往往得不偿失, 可能会引入错误,还会拖累项目交付时间
7.5从设计模型生成代码 从设计模型生成代码的映射规则如下: 1.类的映射 在设计类图中定义的每一个类将被映射到代码中的类。 2.属性的映射 设计类中定义的属性将直接映射为代码中的属性。不同编程语言中数据类型 有所差异,在映射时需要考虑这种差异。比如说Java中的Date类型包含了通常 所指的日期和时间。 3.方法的映射 方法一般进行直接映射,有两种方法需要特殊处理: (1)创建方法映射成相应语言中的构造函数: (2)集合对象的方法需要转换成所采用的集合类所具有的方法。 4.类的关系的映射 类的关系有依赖、关联、聚合、组合和继承。从耦合强度的角度看,依次增 强。它们在实现为代码时,也都有相应的体现 (1)依赖关系 依赖关系有如下三种情况: 1、A类是B类中的(某中方法的)局部变量: 2、A类是B类方法当中的一个参数: 3、A类是一个全局对象,B类的对象访问该全局对象的参数。 图7-2为一个依赖关系的例子,其在代码中的体现为Facility作为一个 Meeting方法的一个输入参数。 Meeting Facility +bookfacility() +book(meeting:Meeting):boolean 图7-2依赖关系 public class Meeting public void bookfacility(Facility facility) facility.book(self) pubic class Facility
7.5 从设计模型生成代码 从设计模型生成代码的映射规则如下: 1. 类的映射 在设计类图中定义的每一个类将被映射到代码中的类。 2. 属性的映射 设计类中定义的属性将直接映射为代码中的属性。不同编程语言中数据类型 有所差异,在映射时需要考虑这种差异。比如说 Java 中的 Date 类型包含了通常 所指的日期和时间。 3. 方法的映射 方法一般进行直接映射,有两种方法需要特殊处理: (1) 创建方法映射成相应语言中的构造函数; (2) 集合对象的方法需要转换成所采用的集合类所具有的方法。 4. 类的关系的映射 类的关系有依赖、关联、聚合、组合和继承。从耦合强度的角度看,依次增 强。它们在实现为代码时,也都有相应的体现 (1) 依赖关系 依赖关系有如下三种情况: 1、A 类是 B 类中的(某中方法的)局部变量; 2、A 类是 B 类方法当中的一个参数; 3、A 类是一个全局对象,B 类的对象访问该全局对象的参数。 图 7-2 为一个依赖关系的例子,其在代码中的体现为 Facility 作为一个 Meeting 方法的一个输入参数。 图 7-2 依赖关系 public class Meeting { public void bookfacility(Facility facility) { facility.book(self) }…… } pubic class Facility {
public boolean book(Meeting meeting) } (2)关联关系 关联一般映射为引用属性。类的引用属性是依据类图中的关联和方向性来的 关联中的角色名字奖杯作为引用属性的名字。关联有不同的类型。 ●双向关联,默认情况下,关联是双向的。在图7-3所示的例子中,两个类内 部都有属性来保存对对方的引用。 +instructs Teacher 1.* 0.* Course +teachers +courses 图7-3关联关系 Public class Teacher public ArrayList courses; public Teacher00 } Public class Course{ public Arraylist teachers; public Course0 0 } ● 单向关联 在关联有方向的情形下,从某一对象出发可以找到箭头所指的类的对象。图 7-4所示的例子中Course内部具有属性来保存相关的teacher对象的引用。 +instructs Teacher 1.* 0.* Course +teachers +courses 图7-4单向关联 Public class Teacher
public boolean book(Meeting meeting) { …… } } (2)关联关系 关联一般映射为引用属性。类的引用属性是依据类图中的关联和方向性来的 关联中的角色名字奖杯作为引用属性的名字。关联有不同的类型。 双向关联,默认情况下,关联是双向的。在图 7-3 所示的例子中,两个类内 部都有属性来保存对对方的引用。 图 7-3 关联关系 Public class Teacher { public ArrayList courses; public Teacher(){} …… } Public class Course{ public Arraylist teachers; public Course() {} …… } 单向关联 在关联有方向的情形下,从某一对象出发可以找到箭头所指的类的对象。图 7-4 所示的例子中 Course 内部具有属性来保存相关的 teacher 对象的引用。 图 7-4 单向关联 Public class Teacher {
public TeacherO0 Public class Course{ public Arraylist teachers; public Course0 ● 自关联 一个类的某些对象可以与另外一些对象有关联关系,例如图7-5所示的例子, 一门课程存在一些前序课程,那么在它的代码内部定义有该类型的对象(数组) 作为属性 +dependson +currentcourse 1 Course 0.* +precourses 图7-5自关联 Public class Coursef public Arraylist precourses; public Course0{ } ●关联的多重性 关联两端的角色具有数量上的对应关系,这称为关联的多重性。其在代码中 一般通过将保存对象引用的属性定义为集合类、数组等得到体现。上述多个例子 中均有体现。 (3)组合和聚合 组合和聚合是一种更强的关联关系。我们在前述已经讨论过它们的区别。图 7-6为一个案例。它们在实现为代码时,有以下一些区别:
public Teacher(){} …… } Public class Course{ public Arraylist teachers; public Course() {} …… } 自关联 一个类的某些对象可以与另外一些对象有关联关系,例如图 7-5 所示的例子, 一门课程存在一些前序课程,那么在它的代码内部定义有该类型的对象(数组) 作为属性 图 7-5 自关联 Public class Course{ public Arraylist precourses; public Course() {} …… } 关联的多重性 关联两端的角色具有数量上的对应关系,这称为关联的多重性。其在代码中 一般通过将保存对象引用的属性定义为集合类、数组等得到体现。上述多个例子 中均有体现。 (3)组合和聚合 组合和聚合是一种更强的关联关系。我们在前述已经讨论过它们的区别。图 7-6 为一个案例。它们在实现为代码时,有以下一些区别:
Rectangle (ordered)/4 Point Format 图7-6类的组合与聚合关系 Public Rectangle public Arraylist Points; public Format format; public Rectangle(float x1,float y1,float x2,float y2,float x3,float y3,float x4, float y4){ Points.add(new point(x1,y1)); Points.add(new point(x2,y2)); Points.add(new point(x3,y3)); Points.add(new point(x4,y4)); } Public SetFormat(Format format){ this.format=format; } 构造函数不同 聚合类的构造函数中包含了另一个类作为参数。Rectangle的构造函数中要 用到Format对象作为参数传递进来。Format可以脱离Rectangle而独立存在。 组合类的构造函数中包含了另一个类的实例化。表明Rectangle在实例化之 前,一定要先实例化Poit类,这两个类紧密的耦合在一起,同生共灭。一个 Rectangle对象内部的Point对象是不可以脱离该Rectangle对象而独立存在的。 ●信息的封装性不同。 在聚合关系中,客户端可以同时了解Rectangle和Format对象,因为它们都 是独立的。在组合关系中,客户端只认识Rectangle对象,根本就不知道它所包 含的Point对象的存在。 (4)继承关系
图 7-6 类的组合与聚合关系 Public Rectangle { public Arraylist Points; public Format format; public Rectangle(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4){ Points.add(new point(x1, y1)); Points.add(new point(x2, y2)); Points.add(new point(x3, y3)); Points.add(new point(x4, y4)); } Public SetFormat(Format format) { this.format = format; } } 构造函数不同 聚合类的构造函数中包含了另一个类作为参数。 Rectangle 的构造函数中要 用到 Format 对象作为参数传递进来。Format 可以脱离 Rectangle 而独立存在。 组合类的构造函数中包含了另一个类的实例化。 表明 Rectangle 在实例化之 前,一定要先实例化 Point 类,这两个类紧密的耦合在一起,同生共灭。一个 Rectangle 对象内部的 Point 对象是不可以脱离该 Rectangle 对象而独立存在的。 信息的封装性不同。 在聚合关系中,客户端可以同时了解 Rectangle 和 Format 对象,因为它们都 是独立的。在组合关系中,客户端只认识 Rectangle 对象,根本就不知道它所包 含的 Point 对象的存在。 (4)继承关系
继承关系是面向对象的基本机制,因而任何面向对象语言都会给以直接支持, 在此略过讨论。 有一种特殊的继承关系一一实现关系,是用来规定接口和实现接口的类的关 系,接口是操作的集合,而这些操作就用于规定类或者构建的一种服务。各种语 言也会有机制来支持,例如Java中就用implement来支持。 Facility +book(meeting:Meeting):boolean Projector Printer 图7-7接口实现 public interface Facility public boolean book(Meetingmeeting); public class Projector implements Facility public boolean book(Meeting meeting) public class Printer implements Facility public void boolean book(Meeting meeting) … } 5.方法体 方法体内的代码可以通过观察消息的顺序,从而生成方法定义中的一系列声 明。 7.6构造过程中的优化 在构造过程中,可能发现设计阶段的不足,从而提出一些典型的修改,包括: 1.调整类的继承结构:当观测到一些类共享属性和方法时,可以评估增加一个 共同父类的必要性; 2.方法的调整:当某一个类中的多个方法具有相似性,可以考虑合并这些方法: 当不同方法中有相同的部分,可以把这一部分提取出来作为一个内部私有方 法供调用: 3.属性的调整:当通过引入属性保存信息能够给实现带来方便性时,评估此必
继承关系是面向对象的基本机制,因而任何面向对象语言都会给以直接支持, 在此略过讨论。 有一种特殊的继承关系——实现关系,是用来规定接口和实现接口的类的关 系,接口是操作的集合,而这些操作就用于规定类或者构建的一种服务。各种语 言也会有机制来支持,例如 Java 中就用 implement 来支持。 图 7-7 接口实现 public interface Facility { public boolean book(Meeting meeting); } public class Projector implements Facility { public boolean book(Meeting meeting) {…… } } public class Printer implements Facility { public void boolean book(Meeting meeting) {…… } } 5. 方法体 方法体内的代码可以通过观察消息的顺序,从而生成方法定义中的一系列声 明。 7.6 构造过程中的优化 在构造过程中,可能发现设计阶段的不足,从而提出一些典型的修改,包括: 1. 调整类的继承结构:当观测到一些类共享属性和方法时,可以评估增加一个 共同父类的必要性; 2. 方法的调整:当某一个类中的多个方法具有相似性,可以考虑合并这些方法; 当不同方法中有相同的部分,可以把这一部分提取出来作为一个内部私有方 法供调用; 3. 属性的调整:当通过引入属性保存信息能够给实现带来方便性时,评估此必
要性。 当采取了优化步骤后,我们需要修改模型以保持一致性。 7.7类与关系数据库表的映射 由于目前关系型数据库是存取数据的主要方式,因此将类映射成相应的表结 构就非常有必要。其规则如下: 1.带有简单数据结构的类,将这些类直接变成表: ●将对象标识符变成主键:为每一个对象生成唯一的标识符,可以作为保存对 象的关系型表中的主键可以使用各种方案使得每一个对象有唯一的D) ●为每一个属性定义相应的字段 2.如果类(集合类)的定义中将其他类(嵌套类)的实例作为属性: ●为嵌套类创建单独的表,嵌套类的对象被分配唯一的对象D ·集合类对应一张表,每个对象具有唯一ID ●创建含有两列的表,第一列保存包含集合的对象的对象标识符:第二列保存 保存在集合中的对象的对象标识符。 3.关联关系的映射: (1)一对多关联:与情形2一样处理 (2)多对多关联:创建一个含有两列的表,每一行包含一对对象标识符,对于 参与关联的对象各有一个; (3)一对一关联:将另一关联的类的标识符作为外键。 4.继承结构 将继承结构映射为关系型数据库表有三种方法: ●只是将超类实现为表,所有子类的额外的属性也将变成超类表的属性,保存 它们没有使用的空值。该方法最适用于子类与超类相比,在行为方面比属性 方面差别更大的情况。需要有ype属性以表明每一行表示哪个子类。 ●只将子类实现为表。超类的属性保存在所有子类的表中。这只是在超类是抽 象的、并且没有实例的情况下可行。 ● 将所有的类(子类和超类)实现为表。要为子类检索数据,子类自己的表和超 类的表都需要访问。同样,在超类表中需要type属性
要性。 当采取了优化步骤后,我们需要修改模型以保持一致性。 7.7 类与关系数据库表的映射 由于目前关系型数据库是存取数据的主要方式,因此将类映射成相应的表结 构就非常有必要。其规则如下: 1. 带有简单数据结构的类,将这些类直接变成表: 将对象标识符变成主键:为每一个对象生成唯一的标识符,可以作为保存对 象的关系型表中的主键(可以使用各种方案使得每一个对象有唯一的 ID) 为每一个属性定义相应的字段 2. 如果类(集合类)的定义中将其他类(嵌套类)的实例作为属性: 为嵌套类创建单独的表,嵌套类的对象被分配唯一的对象 ID 集合类对应一张表,每个对象具有唯一 ID 创建含有两列的表,第一列保存包含集合的对象的对象标识符;第二列保存 保存在集合中的对象的对象标识符。 3. 关联关系的映射: (1) 一对多关联:与情形 2 一样处理 (2)多对多关联:创建一个含有两列的表,每一行包含一对对象标识符,对于 参与关联的对象各有一个; (3)一对一关联:将另一关联的类的标识符作为外键。 4. 继承结构 将继承结构映射为关系型数据库表有三种方法: 只是将超类实现为表,所有子类的额外的属性也将变成超类表的属性,保存 它们没有使用的空值。该方法最适用于子类与超类相比,在行为方面比属性 方面差别更大的情况。需要有 type 属性以表明每一行表示哪个子类。 只将子类实现为表。超类的属性保存在所有子类的表中。这只是在超类是抽 象的、并且没有实例的情况下可行。 将所有的类(子类和超类)实现为表。要为子类检索数据,子类自己的表和超 类的表都需要访问。同样,在超类表中需要 type 属性