构件是系统中
实际存在的可更换部分。它实现特定的功能,符合一套接口标准并能实现一组接口。在图型中,构件表示为一个带有标签的矩形。
机械领域
机器中每一个独立的运动单元体称为一个构件。
[structural member]∶机构的组成单个元,它是一个和某相邻构件有相对运动的刚体。
桥梁构架;抗压构件的设计。
[component part]∶组成部(分)。
电动机的各种构件人财产。
在
机构学中组成机构的、彼此间具有确定的相对运动关系的基本单元,如
曲柄滑块机构中的曲柄、连杆、滑块和机架,
凸轮机构中的凸轮、从动杆和机架。在结构学中则指结构物中的计算或制造单元,它们是固定在一起的,彼此间除由于应变有微量位移外,没有相对运动,如梁、柱、拉杆等。
软件工程
构件是面向软件体系架构的可复用软件模块。构件(component)是可复用的软件组成成份,可被用来构造其他软件。它可以是被封装的对象类、类树、一些功能模块、软件框架(framework)、
软件构架(或
体系结构Architectural)、文档、分析件、设计模式(Pattern)等。1995年,Ian Graham给出的构件定义如下:构件(Component)是指一个对象(接口规范、或
二进制代码),它被用于复用,接口被明确定义。构件是作为一个逻辑紧密的程序代码包的形式出现的,有着良好的接口。像Ada的Package、Smalltalk-80和C++的class和数据类型都可属于构件范畴。但是,操作集合、过程、函数即使可以复用也不能成为一个构件。开发者可以通过组装已有的构件来开发新的应用系统,从而达到
软件复用的目的。软件
构件技术是软件复用的关键因素,也是软件复用技术研究的重点。
软件构件应具备以下属性
(1)有用性(Usefulness):构件必须提供有用的功能;
(2)可用性(Usability):构件必须易于理解和使用;
(3)质量(Quality):构件及其变形必须能正确工作;
(4)适应性(Adaptability):构件应该易于通过参数化等方式在不同语境中进行配置;
(5)可移植性(Portability):构件应能在不同的硬件运行平台和
软件环境中工作。
日历、
工作流构件、订单构件、用户界面控制等等都可以是构件。
2.1.3 构件的特点
构件具有以下几个特点:
(1)自描述:构件必须能够识别其属性、存取
方法和事件,这些信息可以使
开发环境将
第三方软件构件无缝地结合起来;
(2)可定制:允许提供一个典型的图形方式环境,软件构件的属性只能通过控制面板来设置;
(3)可集成:构件必须可以被编程语言直接控制。构件也可以和
脚本语言或者与从代码级访问构件的环境连接,这个特点使得软件构件可以在非可视化开发项目中使用;
(4)连接机制:构件必须能产生事件或者具有让程序员从语义上实现相互连接的其他机制。
采用构件软件不需要重新编译,也不需要
源代码并且不局限于某一种编程语言。该过程叫做二进制复用(Binary Reuse),因为它是建立在接口而不是源代码级别的复用之上的。虽然软件构件必须遵守一致的接口,但是它们的内部实现是完全自动的。因此,可以用过程语言和
面向对象语言创建构件。
由于
构件技术是由基于
面向对象技术而发展起来的,与面向对象的设计中的对象相类似,它们都是针对
软件复用,都是被封装的代码,但它们之间仍存在很大差异。
差别
(1)在纯
面向对象的设计中,对象(类)、封装和继承三者缺一不可,但对构件可以没有继承性,只要实现封装即可;
(2)从构件和对象的生成方式上,对象生成属于实例化的过程,比较单一,而生成构件的方式较多;
(3)构件是设计的概念,与具体编程语言无关,不像对象属于编程中的概念,要依赖于具体的编程语言;
(4)在对构件操作时不允许直接操作构件中的数据,数据真正被封装了。而对象的操作通过公共接口部分,这样数据是可能被访问操作的;
(5)对象对
软件复用是通过继承实现的,构件对软件复用不仅可以通过继承还可以通过组装时的引用来实现。
因此,构件不是对象,只是与对象类似。
计算机领域
构件代表系统中的一部分物理实施,包括
软件代码(
源代码、二进制代码或可执行代码)或其等价物(如脚本或命令文件)。
使用
设计中的
类和对象被作为部署构件进行实施。您需要确定如何将设计类映射为代码;这应该在项目专用的设计指南中有所说明。
有关如何将设计类映射为代码的详细信息,请参见活动:实施构件。另请参见指南:类。
实施构件与修改构件在项目的
配置管理环境中进行。实施员在为他们提供的专用开发工作区(请参见活动:创建开发工作区)中,按照工件:工作单所指定的内容开展工作。在该工作区中,创建源元素并将其置于配置管理之下,或者在通常的检出、编辑、构建、
单元测试、检入周期中进行修改(请参见活动:
进行变更)。完成某个构件集(根据一个或多个工作单的定义以及即将生成的工作版本要求)后,实施员将把有关新的和修改过的构件交付(请参见活动:交付变更内容)到子系统集成工作区,以便与其他实施员的工作进行集成。最后,实施员可以在方便的时候对专用开发工作区进行更新(或者重新调整
基线),使该工作区与子系统集成工作区保持一致(请参见活动:更新工作区)。
当实施类时,应遵循编程指南。
实施的主要基础是具有公有操作、属性与关联关系的类。务必要注意,并不是所有公有操作、属性与关联关系都在设计过程中定义。
实施的辅助基础是用例实现,用例实现显示了
类和对象如何通过交互来执行用例。
最好以递增的方式实施类;编译、链接和运行一些
回归测试,每天进行三两次。
在
从零开始实施一个类之前,可考虑修改现有的实施类(一般可通过建立子类或进行实例化来修改)。
实施操作
要实施操作,请执行以下步骤:
选择算法
选择适合算法的数据结构
根据需要定义新的类和操作
编写操作代码
选择算法
许多操作都十分简单,可以从该操作及其规约中立即实施。
之所以需要特殊算法,主要是为了实施定义了规约的复杂操作,并优化那些以简单但却低效的算法为定义的操作。
选择适合算法的数据结构
选择算法包括选择算法所基于的数据结构。许多实施数据结构是容器类,例如
数组、列表、队列、栈、集合、无序单位组,以及这些类的各种不同形式。许多
面向对象的语言和
编程环境都提供了具有这些可复用构件的类库。
根据需要定义新的类和操作
比如,可以使用新类来保存中间结果,也可对类添加新的低级操作来分解复杂操作。通常,这些操作是类的私有操作,所以在类之外看不见这些操作。
编写操作代码
要编写操作的代码,可从接口语句开始,例如C++中的成员函数声明、Ada中的子程序规约或Visual Basic中的方法。请遵循编程指南。
实施状态
对象的状态可通过引用其属性值来实施,而不必作特殊说明。这种对象的状态转移将隐含于变化的属性值中,而变化的行为通过条件语句来编程。但对于复杂行为,该方法不能令人满意,因为它往往会导致复杂的结构;而当添加更多状态或当行为发生变化时,将很难更改这些结构。
如果构件(或其组成部分)的行为随状态而定,则通常会有一个或多个
状态图来说明组成该构件的模型元素的行为。这些状态图可用作实施过程中的重要输入。有关详细信息,请参见指南:状态图。
状态图中所示的
状态机将表现对象的状态,并详尽说明状态转移及所需的行为。可以通过以下几种方法来实施状态机:
对于简单的状态机,定义一项列举可能状态的属性,然后使用该属性在Java或C++中的switch语句中选择进入消息的行为。但这种方法不太适用于复杂的状态机,它可能会导致运行时性能降低。如需此方法的示例,请参见[DOUG98],第4章 4.4.3
对于较复杂的
状态机,可使用状态模式。有关状态模式的说明,请参见[GAM94]。[DOUG98],第6章 6.2.3 状态模式也说明了这种方法
表驱动法对于极复杂的状态机十分有效,其特点是易于变更。当使用这种方法时,各个状态在表中都有相应的条目,这些条目将输入映射到后继状态和相关的转移动作。如需此方法的示例,请参见[DOUG98],第6章 6.2.3 状态表模式。
要实施具有并行子状态的状态机,可以将状态管理委派给主动对象(每个对象都被委派一个并行子状态),因为并行子状态代表了独立的计算(但仍可能进行交互)。每个子状态均可通过上述方法之一来进行管理。
通过委托关系复用实施
如果一个类或一个类的某些部分可通过复用现有类来实施,则应通过委托关系(而不要继承)来实现。
委托表示一个类借助于其他类来得以实施。该类通过使用变量来引用其他类的对象。当调用某操作时,该操作将调用被引用对象(属于被复用的类)中的操作,以实际执行该操作。这样,它就将职责委派给了其他类。
关联关系
单向关联关系将作为指针(包含对象引用的属性)来进行实施。如果多重性为一,则将单向关联关系当作简单指针来实施。如果多重性为多个,则将其当作指针集来实施。但如果“多”端是有序排列的,则可以使用列表,而不使用集合。
双向关联关系将作为属性,使用单向关联关系的技术在两个方向上实施。
限定关联关系将作为限定对象中的查询表(如一个Smalltalk Dictionary类)来实施。查询表中的选择器值是限定词,而目标值是另一个类的对象。
如果必须按顺序访问限定词的值,就应将限定词组织成经过排序的
数组或树。在这种情况下,访问时间将与log N成比例,其中N为限定词值的数目。
如果限定词取自于一个紧凑的有限集,就可以将限定词的值映射到一个整数范围,并将关联关系当作数组来有效地进行实施。如果关联关系已基本上填满(而不是稀疏填充),此方法会更加有效;而对于完全填满的有限集,它可以算是理想的方法。
许多
面向对象的语言和编程环境都提供了具有可复用构件的类库,可用于实施不同种类的关联关系。
实施属性
可以作为内置
基本变量、可复用的构件类或定义一个新类来实现属性。定义新类通常是较为灵活的方法,但它却会带来不必要的间接性。例如,实施雇员的社会保障号时,既可将它作为类型“字符串”的属性,也可将它作为一个新类。另一种可能的情况是:属性组组成了新类。
向设计提供反馈
在以上任何步骤中,如果发现了设计错误,都必须向设计提供返工反馈。如果所需的变更较小,就可以由同一个人来设计并实施类,而无需提出正式的变更请求。他可在设计中
进行变更。
如果所需的变更影响到几个类(例如在公有操作中的变更),则应向CCB(
变更控制委员会)提交正式的变更请求。请参见活动:修复缺陷。
评估代码
在开始
单元测试之前,可以先作一些检查。测试是一项花费较多的工作,因此最好先执行以下几项检查:
始终对代码进行编译。将
编译器的警告等级设置到最详细的程度。
通过想像对操作进行检查。通读代码,尽可能考虑到所有情况,发现各种异常情况。一旦进行了新的实施活动,就需进行此项工作。
使用工具检查代码中是否存在错误。例如,使用静态代码规则检查程序。
链接库例如 .dll 文件
Applet 例如 Java 中的 .class 文件
Web 页面例如 .htm 和 .html 文件
数据库表
工作产品构件的示例
源代码文件例如C++和CORBA IDL中的.h、.cpp和.hpp文件,或Java中的.java文件
二进制文件例如链接到
可执行文件的.o文件和.a文件。SOM文件IDL和一些绑定编译文件例如UNIX中的makefile
使用
设计中的类和对象被作为部署构件进行实施。您需要确定如何将设计类映射为代码;这应该在项目专用的设计指南中有所说明。
有关如何将设计类映射为代码的详细信息,请参见活动:实施构件。另请参见指南:类。
实施构件与修改构件在项目的
配置管理基线),使该工作区与子系统集成工作区保持一致(请参见活动:更新工作区)。
当实施类时,应遵循编程指南。
实施的主要基础是具有公有操作、属性与关联关系的类。务必要注意,并不是所有公有操作、属性与关联关系都在设计过程中定义。
实施的辅助基础是用例实现,用例实现显示了类和对象如何通过交互来执行用例。
最好以递增的方式实施类;编译、链接和运行一些
回归测试,每天进行三两次。
在
从零开始实施一个类之前,可考虑修改现有的实施类(一般可通过建立子类或进行实例化来修改)。
实施操作
要实施操作,请执行以下步骤:
选择算法 选择适合算法的数据结构 根据需要定义新的类和操作 编写操作代码 选择算法许多操作都十分简单,可以从该操作及其规约中立即实施。
之所以需要特殊算法,主要是为了实施定义了规约的复杂操作,并优化那些以简单但却低效的算法为定义的操作。
选择适合算法的数据结构选择算法包括选择算法所基于的数据结构。许多实施数据结构是容器类,例如
数组、列表、队列、栈、集合、无序单位组,以及这些类的各种不同形式。许多面向对象的语言和编程环境都提供了具有这些可复用构件的类库。
根据需要定义新的类和操作比如,可以使用新类来保存中间结果,也可对类添加新的低级操作来分解复杂操作。通常,这些操作是类的私有操作,所以在类之外看不见这些操作。
编写操作代码要编写操作的代码,可从接口语句开始,例如C++中的成员函数声明、Ada中的子程序规约或Visual Basic中的方法。请遵循编程指南。
实施构件工作流程明细
实施状态
对象的状态可通过引用其属性值来实施,而不必作特殊说明。这种对象的状态转移将隐含于变化的属性值中,而变化的行为通过条件语句来编程。但对于复杂行为,该方法不能令人满意,因为它往往会导致复杂的结构;而当添加更多状态或当行为发生变化时,将很难更改这些结构。
如果构件(或其组成部分)的行为随状态而定,则通常会有一个或多个状态图来说明组成该构件的模型元素的行为。这些状态图可用作实施过程中的重要输入。有关详细信息,请参见指南:状态图。
状态图中所示的状态机将表现对象的状态,并详尽说明状态转移及所需的行为。可以通过以下几种方法来实施状态机:
对于简单的状态机,定义一项列举可能状态的属性,然后使用该属性在Java或C++中的switch语句中选择进入消息的行为。但这种方法不太适用于复杂的状态机,它可能会导致运行时性能降低。如需此方法的示例,请参见【DOUG98】,第4章 4.4.3 对于较复杂的状态机,可使用状态模式。有关状态模式的说明,请参见【GAM94】。【DOUG98】,第6章 6.2.3 状态模式也说明了这种方法 表驱动法对于极复杂的状态机十分有效,其特点是易于变更。当使用这种方法时,各个状态在表中都有相应的条目,这些条目将输入映射到后继状态和相关的转移动作。如需此方法的示例,请参见【DOUG98】,第6章 6.2.3 状态表模式。要实施具有并行子状态的状态机,可以将状态管理委派给主动对象(每个对象都被委派一个并行子状态),因为并行子状态代表了独立的计算(但仍可能进行交互)。每个子状态均可通过上述方法之一来进行管理。
通过委托关系复用实施
如果一个类或一个类的某些部分可通过复用现有类来实施,则应通过委托关系(而不要继承)来实现。
委托表示一个类借助于其他类来得以实施。该类通过使用变量来引用其他类的对象。当调用某操作时,该操作将调用被引用对象(属于被复用的类)中的操作,以实际执行该操作。这样,它就将职责委派给了其他类。
实施关联关系
单向关联关系将作为指针(包含对象引用的属性)来进行实施。如果多重性为一,则将单向关联关系当作简单指针来实施。如果多重性为多个,则将其当作指针集来实施。但如果“多”端是有序排列的,则可以使用列表,而不使用集合。
双向关联关系将作为属性,使用单向关联关系的技术在两个方向上实施。
限定关联关系将作为限定对象中的查询表(如一个Smalltalk Dictionary类)来实施。查询表中的选择器值是限定词,而目标值是另一个类的对象。
如果必须按顺序访问限定词的值,就应将限定词组织成经过排序的数组或树。在这种情况下,访问时间将与log N成比例,其中N为限定词值的数目。
如果限定词取自于一个紧凑的有限集,就可以将限定词的值映射到一个整数范围,并将关联关系当作数组来有效地进行实施。如果关联关系已基本上填满(而不是稀疏填充),此方法会更加有效;而对于完全填满的有限集,它可以算是理想的方法。
许多面向对象的语言和编程环境都提供了具有可复用构件的类库,可用于实施不同种类的关联关系。
实施属性
可以作为内置基本变量、可复用的构件类或定义一个新类来实现属性。定义新类通常是较为灵活的方法,但它却会带来不必要的间接性。例如,实施雇员的社会保障号时,既可将它作为类型“字符串”的属性,也可将它作为一个新类。
属性的备选实施。
另一种可能的情况是:属性组组成了新类,如下例所示。这两种实施都是正确的。
将Line中的属性当作Point类的关联关系来实施。
向设计提供反馈
在以上任何步骤中,如果发现了设计错误,都必须向设计提供返工反馈。如果所需的变更较小,就可以由同一个人来设计并实施类,而无需提出正式的变更请求。他可在设计中进行变更。
如果所需的变更影响到几个类(例如在公有操作中的变更),则应向CCB(
变更控制委员会)提交正式的变更请求。请参见活动:修复缺陷。
评估代码
在开始单元测试之前,可以先作一些检查。测试是一项花费较多的工作,因此最好先执行以下几项检查:
始终对代码进行编译。将
编译器的警告等级设置到最详细的程度。通过想像对操作进行检查。通读代码,尽可能考虑到所有情况,发现各种异常情况。一旦进行了新的实施活动,就需进行此项工作。使用工具检查代码中是否存在错误。例如,使用静态代码规则检查程序。
其他定义
一个理解术语直观含义的方法就是列举它的所有特性。具体的做法是这样的:如果一样东西具有属性a1、a2和a3,它就是A。例如,在著名的Wegner定义(1987)里,如果一种语言支持对象、类和继承,那么它就可以被认为是
面向对象的。
构件的特性是:
# 独立部署单元;
# 作为第三方的组装单元;
# 没有(外部的)可见状态。
这些特性有几种含义。一个构件是独立可部署的,意味着它必须能跟它所在的环境及其他构件相分离。因此,构件必须封装自己全部内部特性。并且,构件作为可部署单元,具有原子性,是不可拆分的。
在这样的约束下,如果第三方厂商能将一个构件和其他构件组装在一起,那么这个构件必须具备良好的
内聚性,还必须将自己的依赖条件和所提供的服务说明清楚。换句话说,构件必须封装它的实现,并且只通过良好定义的接口和外部环境进行交互。
最后,一个构件不能有任何(外部的)可见状态——这要求构件不能与自己的拷贝有所区别。但对于不影响构件功能的某些属性则没有这种限制。