本文的目的是介绍实际的思想,而不是提供严格和精确的定义。面向对象是一种系统建模的技术。它提供了一些概念, 非常适合这个目的。这里使用系统一词的意义很广泛,可以是一个专用的软件系统,也可以是更广泛上下文中的系统(例如, 集成的软硬件系统或组织)。
基于面向对象的思想,我们将系统建模为多个相互作用的对象。因此,无论被建模的系统类型如何,我们将其内容视为, 许多以某种方式相关的对象,例如人、树木、汽车、城镇和房屋等。因此,对象建模的内容取决于我们希望用我们的对象模型表示什么。 我们环境的另一个模型可能包括税收、政府和政治等对象。因此,我们在模型中包含的对象取决于对象模型要表示的内容。
人们以对象的方式看待他们的环境。因此,在设计模型时以相同的方式思考是很简单的。使用面向对象技术设计的模型通常很容易理解, 因为它可以直接与现实关联。因此,使用这种设计方法,现实与模型之间的语义差距很小。
使用面向对象方法设计的系统的最显著特征包括:
- 由于系统与现实之间的语义差距很小,对系统的理解更容易;
- 对模型的修改往往是局部的,因为它们通常来自一个单独表示的项目,即一个对象;
这个介绍只是一个概述,独立于所使用的编程语言和开发方法。我们将不提供任何精确和正式的概念定义,但希望为您提供对这些概念的良好理解。 我们将使用在面向对象环境中最常用的概念和含义。
我们描述的现实涉及进行某些活动的许多人。我们的任务是尝试对这个系统建模。我们将看到,构建一个模型来模拟这个现实是非常自然的。 (在这个描述中,我们使用'人'和'对象'来指代同一物体;实际上,我们指的是代表人的对象。一如既往,我们应该小心区分现实和模型。)
“对象”一词被误用,并且在几乎所有的上下文中都被使用。我们所说的对象是一种能够保存状态(信息)并提供许多操作(行为) 以检查或影响此状态的实体。
对象具有一些操作和一个状态,该状态记住了这些操作的效果。
面向对象的模型由许多对象组成;这些对象是被建模系统的清晰划定的部分。对象通常对应于现实生活中的实体对象,如发票、汽车或手机。 每个对象包含个体信息(例如,汽车有其注册号码)。
在我们创建的系统中,将有许多相互通信的对象。其中一些对象将具有共同的特征,我们可以根据这些特征对对象进行分组。当我们查看示例中的对象时, 我们注意到所有三个人都具有相似的行为和信息结构。这些对象具有相同的模具或模板。这样的一组对象代表一个类。为了描述所有具有相似行为 和信息结构的对象,因此我们可以确定并描述一个类来表示这些对象。
类是一种定义、模板或模型,用于创建新对象,因此是对多个对象的共同特征的描述。组成某个类的对象共享这个模板。例如,我们可以看待这本 书。你手中拿着的书是这本书的一个实例。出版商的书籍描述代表了可以创建实例的类。
- 一个类表示多个对象的模板;
- 描述这些对象在内部结构上的构造;
- 同一类的对象对于它们的操作和信息结构都有相同的定义;
类有时被称为对象的类型。然而,类型和类并不是相同的东西。如上所述,抽象数据类型是由一组操作定义的。类型是由你可以对类型进行的操纵来定义的。 类不仅仅是这样。你还可以查看类的内部,例如,查看它的信息结构。因此,我们更愿意将类视为类型的一种(可能是许多种)具体实现。
使用类的概念,我们可以将某些特征与整个对象组相关联,我们可以将类视为描述组成该类的对象的所有共同特征的抽象。
在面向对象的系统中,每个对象都属于一个类。属于某个类的对象称为该类的实例。因此,我们经常将对象和实例视为同义词。
实例是从类创建的对象。类描述了实例的(行为和信息)结构,而实例的当前状态由对实例执行的操作定义。
从类创建的实例将共同为我们提供希望建模的动态行为。当这些实例开始相互通信时,系统的行为就会执行。一个实例可能知道可以向其发送刺激的 其他实例。如果一个实例向另一个实例发送消息,但不必知道接收实例属于哪个类,我们称之为多态性。多态性在对象导向的上下文中,表示发送实例 不需要知道接收实例的类,而且这个类可以是任何类。多态性意味着消息的发送者:
- 不需要知道接收实例的类;
- 接收实例可以属于任意类;
如果类 B 继承自类 A,那么类 A 中描述的操作和信息结构将成为类 B 的一部分。通过继承,我们可以展示类之间的相似之处, 并在一个类中描述这些相似之处,其他类可以继承这些描述。因此,我们可以重用共同的描述。因此,在软件行业,继承通常被提升为重用的核心思想。 然而,尽管继承在许多上下文中使用得当(包括重用),它并不是重用的必要条件。
面向对象分析的目的,与所有其他分析一样,是获得对应用程序的理解:这种理解仅依赖于系统的功能要求。面向对象分析包括以下活动, 其顺序可能有所不同:
- 找到对象: 识别和定义系统中的对象,这些对象是问题域中实际存在的实体;
- 组织对象: 对对象进行组织,可能涉及到将它们划分为类别或类;
- 描述对象之间的交互: 定义对象如何相互作用,包括它们之间的关系、通信方式等;
- 定义对象的操作: 确定对象可以执行的操作(方法或函数),以满足系统的功能需求;
- 定义对象的内部结构: 描述对象的内部结构,包括属性、数据和其他成员;
这些活动的目标是捕捉系统的核心特征,从而为进一步的设计和实施提供基础。这有助于建立一个抽象的模型,该模型反映了系统中 存在的重要实体以及它们之间的关系。在此过程中,也可能涉及对系统的用例进行分析以理解外部实体与系统之间的交互。
在应用领域中,对象可以被自然地发现为应用程序中的实体。目标是找到那些在整个系统生命周期中始终保持基本的实体。稳定性还取决于这样一个事实, 即修改通常从其中一些项目开始,因此是局部的。例如,在一个用于控制水箱的应用程序中,典型的对象可能包括包含的水、调节器、阀门和水箱。
自上世纪 70 年代出现以来,面向对象分析已在多种不同的上下文中被使用,例如信息管理系统和组织理论的分析。其目的是创建要分析的 系统或组织的模型。概念建模的概念通常被用作数据建模的同义词,并经常与结构化和数据库的使用一起讨论。
首先进行的转换是从需求规格到需求模型。需求模型包括:
- 用例模型;
- 接口描述;
- 问题域模型; 用例模型使用了演员(actors)和用例(cases)。这些概念只是用于定义系统外部存在的内容(演员)和系统应该执行的操作(用例)。
我们已经看到,需求模型的目标是定义系统的限制并规定其行为。当需求模型已经由系统用户或订购方开发并获得批准后,我们可以开始开发实际的系统。
这始于分析模型的开发。其模型的目标是独立于实际的实施环境对系统进行结构化。这意味着我们关注系统的逻辑结构。在这里,我们定义了稳定、 健壮且可维护的结构,同时也具有可扩展性。
在构建过程中,我们使用分析模型和需求模型构建系统。首先,我们创建一个设计模型,这是对分析模型的细化和形式化。
设计强调满足需求的概念性解决方案,而不是其实现。例如,数据库架构和软件对象的描述。最终,设计可以被实现。与分析一样, 术语最好是有资格的,比如对象设计或数据库设计。分析和设计已经在“做正确的事情”(分析)和“以正确的方式做事情”(设计)阶段总结。
实现模型包括带注释的源代码。信息空间是编程语言使用的空间。请注意,我们不需要一种面向对象的编程语言;该技术可以使用任何编程语言来 获得系统的面向对象结构。然而,面向对象的编程语言是可取的,因为所有基本概念可以轻松映射到语言构造。
测试模型是系统开发中开发的最后一个模型。简单地说,它描述了测试的结果。测试中的基本概念主要是测试规范和测试结果。
分析强调对问题和需求的调查,而不是解决方案。例如,如果需要一个新的计算机化图书馆信息系统,那么它将如何使用?“分析”是一个广义的术语, 最好是限定为需求分析(对需求的调查)或对象分析(对领域对象的调查)。
在分析阶段,开发了两种不同的模型,即需求模型和分析模型。真实的基础是需求规格和与潜在用户的讨论。例如,该系统控制着用于可回收瓶子、 罐头和箱子的回收机(在欧洲用于容纳多个瓶子)。
在构建阶段,我们基于在分析阶段创建的分析模型和需求模型来构建我们的系统。构建过程一直持续到编码完成并且代码单元经过测试。 构建包括设计和实现。
对象导向技术是建立在坚实的工程基础之上的,我们统称为对象模型。对象模型涵盖了抽象、封装、模块化、层次结构、类型、并发和持久性等原则。 对象模型之所以重要,是因为这些元素以一种协同的方式结合在一起。
毫无疑问,对象导向分析和设计与传统的结构化设计方法有根本的区别。它需要一种不同的分解思维方式,并且产生的软件体系结构大部分超出了 结构化设计文化的范畴。这些差异源于结构设计方法建立在结构化编程的基础上,而对象导向设计则建立在对象导向编程的基础上。 对象导向编程对不同的人有不同的含义。
在我们回顾软件工程相对短暂但丰富多彩的历史时,我们不禁注意到两个深刻的趋势。一个是从小规模编程转向大规模编程的关注。 另一个是高级编程语言的演变。
结构化设计方法的演变是为了指导那些试图使用算法作为基本构建块构建复杂系统的开发人员。同样,面向对象设计方法的演变是为了 帮助开发人员利用基于类和对象的编程语言的表达能力,将类和对象作为基本构建块。因此,面向对象的分析和设计代表了一种渐进的发展, 而非革命性的发展;它并没有摒弃过去的进步,而是在其基础上进行了建设。
不幸的是,今天大多数程序员在正式和非正式培训中只接受了结构化设计原则的教育。
面向对象编程是一种实现方法,其中程序被组织为对象的协同集合,每个对象都代表某个类的实例,这些类都是通过继承关系联合在一起的类层次结构的成 员。面向对象编程使用对象而不是算法作为其基本的逻辑构建块。每个对象都是某个类的实例。类之间通过继承关系相关联。一个程序可能看起来是面向对象 的,但如果其中任何元素缺失,它就不是一个面向对象的程序。
它支持具有名称操作接口和隐藏本地状态的数据抽象的对象。
- 对象有一个关联的类型(类);
- 类型(类)可以从超类型(超类)继承属性;
面向对象设计是一种设计方法,包括面向对象分解的过程和一种表示正在设计的系统的逻辑,和物理模型以及静态和动态模型的符号。 这个定义有两个重要部分:
- 面向对象设计导致面向对象的分解;
- 它使用不同的符号来表示系统的逻辑,和物理设计的不同模型,以及系统的静态和动态方面;
面向对象分析是一种分析方法,从问题域的词汇中的类和对象的角度检查需求。
OOA、OOD 和 OOP 之间的关系如何?基本上,面向对象分析的产品可作为我们开始面向对象设计的模型;面向对象设计的产品然后可以用作, 使用面向对象编程方法完全实现系统的蓝图。
- 过程导向的算法。
- 面向对象的类和对象。
- 逻辑导向目标,通常用谓词演算表达。
- 规则导向如果则规则。
- 约束导向不变关系。
每种编程风格都基于自己的概念框架。每种都需要不同的思维方式,不同的问题思考方式。对于所有面向对象的东西,概念框架就是对象模型。
对象模型有四个主要元素:
- 抽象
- 封装
- 模块化
- 层次结构
对象模型还有三个次要元素:
- 类型
- 并发
- 持久性
面向对象的分析和设计可能是我们今天用来应对非常大系统中固有复杂性的唯一方法。然而,公正地说,面向对象开发的使用对于某些领域可能是不明智的, 原因不是技术原因,而是非技术原因,比如没有合适培训的员工或良好的开发环境。
为了了解面向对象分析与设计(OOAD)的优劣,您需要知道OO方法论在软件生命周期中的位置。这些方法论并不取代传统方法(如数据流、过程流和状态转换图),而是重要的新工具。
根据Donald Firesmith在他的书《Dictionary of Object Technology》(SIGS Books,1995)中的说法,分析是“开发活动,包括需求的发现、建模、规范和评估”,而OO分析是“发现、分析和以对象为中心的需求规范,这些对象具有封装属性和操作、消息传递、类、继承、多态和动态绑定。” Firesmith还指出OO设计是“根据对象、类、集群、框架及其相互作用设计应用程序。”
在将传统分析的定义与OOAD的定义进行比较时,真正新颖的方面是以对象和对象类的术语思考世界或问题。类是一组逻辑相关的实例的唯一标识抽象(即模型),这些实例共享相同或相似的特征。对象是对单个事物建模的抽象,术语“对象”与实例是同义的。类具有属性和方法。例如,名为Customer的对象类的属性可能是Name和Address,而方法可能是Add、Update、Delete和Validate。类定义定义了Customer类的属性和方法,而“XYZ Corp.”等真实客户是该类的实例。如果您有不同类型的客户,例如住宅客户和商业客户,可以创建两个Customer类的子类。这些子类使用继承来访问所有Customer类的属性和方法,但可以覆盖任何祖先的属性和方法,同时包含任何需要的新属性和方法。
类之间有三种关系:继承、聚合和关联。继承(也称为一般化/特殊化)通常由短语“是一种”标识。例如,Student和Faculty都是Person的一种,因此从Person类继承。聚合由短语“是的一部分”标识,如包含部件的产品。如果前两种关系都不适用,但对象明显相关(例如,雇员与公司相关联),则关系是关联。
抽象类是没有实例的类,仅用于继承。具体类是可以实例化的类,即可以具有直接实例。
所有主要的OOAD方法论在对象、类、继承和关系方面有相似的基本观点,绘图符号在每个方法论中略有不同,方法论的真正差异更为微妙。
选择方法论时,重要的是考虑方法论的特性、使用成本、最适合的问题类型、局限性以及可用的培训。在使用OOAD方法论进行开发客户/服务器应用程序的典型初次尝试时,所有方法论都存在相同的基本缺陷:
- 过度强调OO方法,即使在问题的某些部分可能有更好的方法。
- 在分析阶段过度强调问题域对象模型。
- 用户难以理解的分析图表和输出格式。
- 方法论描述复杂分析问题的能力困难。
- 对底层系统架构的重视不足。
- 无法理解4GL OO语言或初学OO开发人员的局限性。
每种对象方法论都告诉您从对象模型开始,而不是数据模型;但这种方法有至少四个问题:
- 数据模型通常存在于对象模型之前。
- 分析员可能更愿意在构建对象模型之前构建数据模型。
- 一个好的对象模型应该能够映射到任何数据模型。对我而言,在复杂系统中,通常需要一个对象的属性可以映射到一个或多个数据库中的一个或多个表。
- 在问题域的良好抽象对象模型可能不容易在选择的语言或开发工具中实现。
OOAD 方法论分为两种基本类型。三元(或三叉)类型是现有结构化方法的自然演变,其具有针对数据、动态和过程的三种单独表示法。一元类型则断言,由于对象结合了过程(方法)和数据,因此只需要一种表示法。一元类型被认为更像对象,并且更容易从零开始学习,但缺点是从分析中产生的输出可能无法与用户审查。
动态建模涉及事件和状态,通常使用状态转换图。过程建模或功能建模涉及将数据值转换的过程,传统上使用数据流图等技术。
格雷迪·布奇(Grady Booch)对面向对象分析与设计(OOAD)的方法是最受欢迎之一,得到了从 Visio 到 Rational Rose 等各种价格合理的工具的支持。布奇是 Rational Software 的首席科学家,该公司生产了Rational Rose。(现在詹姆斯·朗博和伊瓦尔·雅各布森已加入该公司,Rational Software成为了面向对象分析与设计领域的主要力量之一)。布奇的设计方法和符号包括四个主要活动和六个符号。
尽管布奇方法涵盖了需求分析和领域分析,但其主要优势在于设计。然而,随着朗博和雅各布森加入,对于分析阶段的相对弱点正在迅速消失。我相信布奇代表了开发得较为完善的 OOAD 方法之一,而且现在 Rational Rose 正在从与 C++ 的紧密联系转向更加开放的方法,支持诸如 PowerBuilder 等 4GL,这将大大提升该方法的流行度。
对于具有复杂规则的系统,状态图对于状态较少的系统是合适的,但对于具有大量状态的系统则不适用。一旦单个状态转换图超过 8 到 10 个状态,就变得难以管理。对于超过 20 个状态的情况,状态转换图变得过于笨重。
建模在所有工程学科中都有着丰富的历史。这种经验表明建模有四个基本原则。
- 创建何种模型对问题的解决方式和解决方案的形成具有深远影响;
- 每个模型都可以以不同精度的级别来表达;
- 最好的模型与现实相连;
- 没有单一的模型是足够的。每个非平凡系统最好通过一小组几乎独立的模型来解决;