面向对象分析与设计(V3)第一章:复杂性

书名(中):面向对象分析与设计
书名(英):Object-Oriented Analysis and Design with Applications
作者:Grady Booch等

第一部分.概念

第一章:复杂性

1.2.2 为什么软件在本质上是复杂的

软件的复杂性是一个基本特征。我们认为这种固有的复杂性有4个原因:

  • 问题域的复杂性
  • 管理开发过程的困难性
  • 通过软件可能实现的灵活性
  • 刻画离散系统行为的问题

1. 问题域的复杂性

​ 航电系统,蜂窝式移动电话交换系统。这些系统的基本功能已经很难理解了,现在还要加上所有的(常常是隐含的)非功能需求,如可用性、性能、成本、健壮性和可靠性。这种无限制的外部复杂性是导致Brooks所说的任意复杂的原因之一。

​ 这种外部复杂性通常源自于系统用户和系统开发者之间的 "沟通困难":用户常常发现很难用开发者能够理解的形式对他们的需求给出准确的表达。在某些情况下,用户只是对想要的软件系统有一个模糊的想法。这既不是系统用户的错,也不是系统开发者的错,出现这种情况是因为这些人都缺乏另一个领域的经验。用户和开发者对问题的本质有着不同的看法,并根据解决方案的本质做出了不同的假定。实际上,即使用户对他们的需求知道得很清楚,我们目前也没有什么好方法来准确地记录下这些需求。常见的描述需求的方法是用一大段文字,偶尔配有一些插图。这样的文档时难以理解的,可能产生不同的解读,而且经常包含一些设计方案,而不是基本需求。

​ 更麻烦的是,软件系统在开发过程中经常发生需求改变。主要是因为软件开发项目本身改变了问题的规则。看到早期的产品如设计文档和原型之后,在安装好并使用系统之后,在强制使用所有功能之后,用户会对他们的需求有更好的理解和表述。同时,这个过程也帮助开发者了解了问题域,使他们能够问出更好的问题,从而照亮系统期望行为中的黑暗角落。

软件开发团队的任务就是制造简单的假象

因为大型的软件系统是一项投资,所以我们不能忍受在每次需求发生变化时,就抛弃掉所有的系统。不管是否有计划,系统都会随时间的推移而演化,这种情况常常被错误地称为软件维护。准确地说,在我们修正错误时,这是维护;在我们应对改变的需求时,这是演化;当我们使用一些极端的手段来保持古老而陈腐的软件继续工作时,这是保护。不幸的是,事实表明相当一部分软件开发资源被用在了软件保护上。

2. 管理开发过程的困难性

大型项目的工作量要求我们启用开发团队。有更多的开发人员就意味着更复杂的沟通,因此更难以协调,特别是当开发团队在地理位置上是分散的时候,而实际情况又常常如此。对于开发团队来说,主要的管理挑战总是维持设计的一致性和完整性。

3. 软件中随处可能出现的灵活性

一家造房屋的公司通常不会自己经营林场、砍伐树木以及获取原木。但在软件行业,这种情况却经常发生。软件提供了非常大的灵活性,所以开发者几乎有可能表达任何形式的抽象。

软件提供了非常大的灵活性,所以开发者几乎有可能表达任何形式的抽象。但是,这种灵活性变成了一种难以置信的诱人的属性,因为它也迫使开发者打造几乎所有的初级构建模块,高层的抽象将建立在这些初级构建模块之上。建筑行业对原材料的品质有着统一的编码和标准,但软件行业却很少有这种标准。

结果,软件行业还是一种劳动密集型的产业。

4. 描述离散系统行为的问题

如果向空中抛出一个球,我们可以肯定地预测出它的路径,因为我们知道在正常的情况下,某些物理定律会起作用。

在大型应用中,可能有成百上千个变量以及多个控制线程。系统中的这些变量、它们当前的值、当前的地址和每个过程的调用栈一起构成了应用当前的状态。因为我们是在数字计算机上执行软件,所以我们的系统具有离散的状态。Parnas指出:“当我们说系统是由连续函数描述的时候,我们是说它不会包含任何隐含的惊奇。输入中的小变化总是会导致输出中相应的小变化”。而在另一方面,离散系统从本质上来说具有有限数量的可能状态。在大的系统中,由于组合的缘故,导致可能状态的数目变得非常大。我们试图以分离关注的方式来设计我们的系统,这样,系统某部分的行为对其他部分行为的影响就能降至最低。但是有一个事实仍未改变,即离散系统中的状态转换不能够用连续函数来建模。软件系统之外的每个事件都有可能让系统进入一个新的状态。而且,状态与状态之间的转换关系并非总是确定的。在最坏的情况下,外部的事件可能会破坏系统的状态,因为它的设计者没有考虑到事件之间的相互作用。

数学工具和我们的智能都不能够对大型离散系统的完整行为进行建模,关于系统的正确性,我们必须满足于可接受的信息级别。

1.3 复杂系统的5个属性

1. 层次结构

复杂性常常以层次结构的形式存在,复杂的系统由一些相关的子系统组成,这些子系统又有自己的系统,如此下去,直到达到某种最低层次的基本组件。

许多复杂系统都有几乎可分解的层次机构,正是这一事实让我们能够理解、描述甚至 '看到' 这样的系统和它们的组成部分。

复杂系统的架构是它所有的组件以及这些组件之间的层次结构的函数,认识到这一点很重要。所有系统都有子系统,所有系统都是更大系统的组成部分,一个系统所提供的价值肯定来自于各个组成部分之间的关系,而不是来自于单个的组成部分。

2. 相对本原

关于复杂系统中基础组件的是指,我们的经验表明:"选择哪些作为系统的基础组件相对来说比较随意,这在很大程度上取决于系统观察者的判断。"

对于一个观察者来说很基础的东西,对另一个观察者可能具有很高的抽象层次。

3. 分离关注

"几乎可分解的",这引出了所有复杂系统的另一个共同属性:

"组件内的联系通常比组件间的联系更强,这一事实实际上将组件中高频率的动作(涉及组件的内部结构)和低频率的动作(涉及组件间的相互作用)分离开来"

组件内部作用和组件间作用的差异让我们在系统的不同部分之间实现 "分离关注",让我们能够以相对隔离的方式来研究每个部分

4. 共同模式

层次结构通常只是由少数不同类型的子系统按照不同的组合和安排方式构成的。

换言之,复杂系统具有共同的模式。这些模式可能涉及小组件的复用,如下包,或者大一些的结构如脉管系统,在植物和动物中都存在。

5. 稳定的中间形式

复杂的系统趋向于随时间而演变。准确地说:“如果存在稳定的中间形式,从简单系统到复杂系统的演变将更快”。用更夸张的词来说:"复杂西塔毫无例外都是从能工作的简单系统演变而来的...从头设计的复杂系统根本不能工作,也不能通过打补丁的方式使其工作,必须从头开始,从能工作的简单系统开始。"

随着系统的演变,曾经被认为是复杂的对象就变成了基础对象,在这些对象的基础上构建更复杂的系统。而且,永远也不能够第一次就正确打造出这些基础对象,必须在上下文环境中使用它们,然后随着时间的推移不断地改进它们,因为我们对系统的真实行为了解得越来越多。

1.4 有组织和无组织的复杂性

1. 复杂系统的规范形式

飞行器分解为推进系统、飞行控制系统等。这种分解代表了结构上或 "组成部分(part of)" 的层次结构。

喷气引擎的子类有涡轮引擎,Pratt and Whitney TF30 又是一种具体的涡轮引擎。涡轮引擎只是一种特殊类型的喷气引擎,它有一些特意的特征,可以和冲压喷气式引擎区分开来。这种分解代表了 "是一种(is a)" 的层次结构。

是一种:类结构

组成部分:对象结构

模块结构:描述系统物理组件之间的关系

处理层次结构:描述系统的动态组件之间的关系

image

将类结构和对象结构的概念和复杂系统的5种属性(层次结构、相对基础(如多层次的抽象)、分离关注、模式和稳定的中间形式)结合起来,我们发现,基本上所有的复杂西塔都具有相同的(规范的)形式,如图 1-2 所示。我们将系统的类结构和对象结构统称为它的 "架构"。

image

还要注意的是,类结构和对象结构不是完全独立的,对象结构中的每个对象都代表了某个类的一个具体实例。(在图1-2中,注意类C3、C5、C7、C8和实例03、05、07、08) 正如在图中所看到的,复杂系统中对象的数量通常比类的数量要多很多。通过展示 "组成部分" 和 "是一种" 层次关系,我们明确地揭示了被研究系统的冗余性。如果不揭示系统的类结构,我们就不得不将属性方面的知识重复地放到每个部分中。通过类结构,我们在一个地方记录了这些共同的属性。

没有一种特定的架构真正被认为是 "正确的"。这也是使得系统架构具有挑战性的原因-在可能的许多种系统组件结构中、复杂系统的5种属性中以及系统用户的需求中寻找平衡。

经验表明,最成功的的复杂软件系统就是在设计中包含了深思熟路的类结构和对象结构,并具备了前一节所描述的复杂系统的5种属性。为了不让这个重要的结论被忽略掉,让我们说得再直接一些:如果不考虑这些因素,我们就不太可能准时地在预算范围之内交付满足需求的软件系统。

2. 处理复杂性时人的能力的局限

在刚开始分析复杂软件系统时,我们发现许多部分以多种交错的方式,这些部分在它们的交互行为之间几乎没有什么显而易见的共性。这就是无组织的复杂性的例子。当我们通过设计的过程对这种复杂性进行组织时,必须同时考虑许多事情。

遗憾的是,一个人绝对没有办法同时追踪所有的细节。心理学家的一些实验(如Miller的实验)表明,一个人能够同时理解的最大信息数量是7个,上下浮动2个。这种渠道能力似乎与短期记忆有关。同时,处理速度也是一个限制因素:大脑需要大约5秒钟才能接受一组新的信息。

因此,我们面对的是一个根本难题:要开发的软件系统的复杂性在增加,而我们处理复杂性的能力却有局限。怎样才能解决这个困境?

1.5 从混沌到有序

1. 分解的作用

1.算法分解

系统中的每个模块代表了某个总体过程的一个主要步骤。

image

2.面向对象的分解
image

每个对象都有它自己的独特性为,每个对象都是真实世界中的某个对象的模型。从这个角度来看,一个对象就是一个可以触摸的实体,展示了一些定义良好的行为。对象能做一些事情,我们通过发送消息要求它们能做的事情。

3.算法分解与面向对象分解

算法的观点强调了事件的顺序。

面向对象的观点强调了一些代理,它们要么发出动作,要么是这些操作执行的对象。

分析和设计方法的分类

我们发现,区分 “方法” 和 “方法学” 这两个术语是有意义的。一种方法是一个有规定的过程,目的是生成一组模型,利用某种定义良好的表示法,描述被开发的软件系统的各个方面。另一种方法学是一组方法,适用于软件开发生命周期的各个阶段,它由过程、实践和某种一般的哲学统一起来。方法很重要,这有几个原因。首先,他们在复杂软件系统的开发中注入了纪律。其次,它们定义了一些产品,这些产品成为了开发团队中的成员进行沟通的载体。另外,方法定义了管理层测量进度和管理风险所需的里程碑。

  • 自顶向下的结构化设计
  • 数据驱动设计
  • 面向对象设计

但是,实际情况确实我们无法同时用两种方法来构建复杂系统,因为它们的观点是完全正交的。开始分解系统时,我们必须要么从算法开始,要么从对象开始,然后利用得到的结构作为框架来表达其他的看法。‘’

经验使我们首先应用面向对象的观点,因为这种方法更有助于组织软件系统的内在复杂性。面向对象分解通过复用共同的机制,得到一些较小的系统。在应对变化时也更有弹性,从而更能够随时间演变,因为他们的设计是基于稳定的中间状态的。通过帮助我们明智地决定对巨大的状态空间进行分离关注,面向对象的分解直接关注了软件的内在复杂性。

2. 抽象的作用

Miller的实验结论:一个人同一时刻只能理解大约7个信息,上下浮动2个。这个数字似乎与信息的内容无关。正如Miller自己说的:"绝对判断的范围和短期记忆的范围对我们能够接收、处理和记住的信息量有着很强的限制。通过将输入组织为一些不同的维度,并形成一些片段序列,我们设法打破...这种信息瓶颈"。用现在的术语来说,我们把这个过程叫做 "分块" 或 "抽象"。

我们已经形成了一种异常强大的技术来对付复杂性。我们对它进行抽象。不能够全面掌握一个复杂的对象,我们就选择忽略它的非本质的细节,转而处理这个对象的一般化的、理想化的模型。

我们仍然受到同时可以理解的事物数量的限制,但通过抽象,我们利用了信息的分块和不断增大的语义内容。对象作为真实世界中实体的抽象,代表了特定的一块密集而内聚的信息。

3. 层次结构的作用

另一种增加单块信息的语义内容的方法,是在复杂的软件系统中显式地组织类和对象层次结构。对象结构很重要,因为它展示了不同的对象之间如何通过一些交互模式进行写作,我们把这些交互模式称为 "机制"。

类结构也同样重要,因为它强调了系统中的公共结构行为。

1.6 复杂系统的设计

2. 设计的含义

在每一种工程实践中,设计都是一种训练有素的方法,我们通过它来创造某个问题的解决方案,从而提供实践需求的途径。在软件工程中,Mostow指出设计的目的是要构建如下的一个系统:

  • 满足给定的(可能是非正式的)功能规格说明
  • 符合目标介质的限制
  • 满足隐含的和明确的性能及资源使用需求
  • 满足隐含的和明确的关于产品形式方面的设计限制条件
  • 满足对设计过程本身的限制条件,如时间、费用或进行设计可用的工具

Stoustrup指出:“设计的目的是创建一个干净的、相对简单的内部结构,有时候也被称为架构...一份设计是设计过程的最终产物”

1.建模的重要性

当设计一台个人计算机时,电子工程师必须考虑系统的组件级视图以及线路板的物理布局。这个组件视图构成了系统设计的逻辑视图,它帮助工程师思考组件间的协作关系。线路板的布局代表了这些组件的物理封装,它受到线路板尺寸、可用电源和组件种类等条件的限制。通过这个视图,工程师可以独立地思考散热和制造等方面的问题。线路板的设计者还必须考虑到在建系统的动态方面和静态方面。因此,电子工程师利用一些图示来展示单个组件之间的静态连接,也用一些时间图来展示这些组件随时间变化的行为。然后,工程师就可以利用示波器和数字分析设备来验证静态模型和动态模型的正确性。

2.软件设计方法学的要素

  • 表示法:表达每个模型的语言
  • 过程:导致有序构建系统模型的过程
  • 工具:消除建模中的枯燥工作并强制实现模型本身的规则的工作,可以揭示错误和不一致性

3.面向对象开发的模型

1.7 小结

  • 软件本质是复杂的,软件系统的复杂性常常超出了人类智能的范围
  • 软件开发团队的任务就是制造出简单的假象
  • 复杂性常常以层次结构的形式表现出来,建立复杂系统的 "是一种" 和 "组成部分" 层次结构模型是有意义的
  • 复杂系统通常是从一些稳定的中间状态演进而来的
  • 人类的认识有一些基本的限制因素,我们可以通过分解、抽象和层次结构来克服这些限制
  • 复杂系统可以从事物或处理过程的角度来分析,采用面向对象的分解有一些另人感兴趣的理由。在这种方法中,将世界看作是一组有意义的对象进行协作,实现某种高级的行为
  • 面向对象分析和设计的方法实现了面向对象分解。面向对象的设计采用了一套表示法和过程来构造复杂软件系统,提供了丰富的模型,可以通过这些模型来阐述目标系统的不同方面。

7.实战

管理人员应该帮助他的开发人员学会制定有效的计划,通常这是只能通过实战经验才能获得的技巧。足够的训练和估算指南对于减少低下计划来说是必须的。

面向对象度量:当你可以测量你要说的东西并将其表达成数字时,你了解一些事情;但当你不能测量它,不能将其表达成数字时,你的知识是贫乏和不尽人意的。这可能是知识的开始,但你几乎没有想到这是迈向科学的阶段。

posted @ 2023-03-06 16:50  LHX2018  阅读(43)  评论(0编辑  收藏  举报