(豁然开朗)《面向对象分析与设计》读书笔记 (1)- 关键的思想

读此书的目的在于,通俗理解面向对象的思想。该书作者为Grady Booch等人,Grady Booch
是UML创始人之一。

该文章首先介绍复杂性系统一些相关的知识,复杂性系统为面向对象这种设计方法诞生提供了场景。自此基础上,进一步说明面向对象的设计方法是如何克服这些复杂性的。随后介绍对象模型,包括对于对象模型基本概念,基本特性的介绍。这些特点是理解一系列面向对象语言的思想基础。

1.复杂性

之所以强调复杂性,是因为复杂性是面向对象主要解决的问题。从编程范式的发展历史中可以看出,随着系统越来越复杂,系统就会变得越来越容易崩溃,编程方法应随之提升,才能解决相应的复杂的问题。

1.1. 软件固有的复杂性

软件复杂性

作者此处所研究的软件特指工业级软件,而工业级软件具有这样的特征,单个开发者要理解其设计的所有方面非常困难,几乎是不可能的

软件本质上是复杂的

Brooks:"软件的复杂性是一个基本特征,而不是偶然如此"。具有这种复杂性的原因有4:

  • 问题域的复杂性。软件是为了解决现实生活中的问题,而现实生活中存在着大量的需求,比如说设计火车订票系统。并且需求是会不断发生变化的,这种外部的复杂性导致了软件的复杂性。
  • 管理开发过程的困难性。开发复杂的系统,不能单打独斗,需要团队协作,但团队协作会带来更多的沟通,协调,这方面的困难也增加了软件的复杂度。
  • 软件中随处可见的灵活性。
  • 描述离散系统行为的问题。

1.2.复杂系统的5个属性

(1)层次结构

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

补充一下:什么叫做层次?层次是指系统在结构或功能方面的等级秩序。平时我们所说的两个人的层次水平不一样,其实是说我们对于事物的认知不同,而人们的认知往往又是层层递进的。另外一个例子,网络协议7层,也很好的说明了层次的重要性。

(2)相对本原

选择哪些行为作为系统的基础组件相对来说比较随意,这在很大程度上取决于系统观察者的判断。

补充一下:基础组件,这里是指构建系统的最小单位。你不需要担心基础组件是如何实现的,只要利用其外部行为即可。举个例子,你要盖一个房子,你需要砖,水泥等,这些都是一些基础组件,但是你不要自己去生产砖,水泥。

(3)分离关注

Simon将层次系统称为“可分解的”,分成各部分之后,这些部分并不完全独立。于是有:

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

(4)共同模式

复杂系统具有共同的模式。比如小组件的复用。设计模式是面向对象方法中常见的处理问题的一些模式。
Simon:"层次结构通常只是由少数不同类型的子系统按照不同的组合和安排方式构成的"。

(5)稳定的中间形式

如果存在稳定的中间形式,从简单系统到复杂系统的演变将更快

复杂系统是在演变中诞生的,不要一开始就期望能够构建出一个复杂的系统。要从简单系统逐步迭代到复杂的系统

1.3.层次结构的形式

(1)组成(”part of“)层次结构。举例,人由手、脚等组成。在面向对象中表现为组合

(2)”是一种“("is a")层次结构。举例,人是动物。在面向对象中表现为继承

练习:选择一个熟悉的事物,通过上面两种方式将其分解为层次结构。

举例,学校,是教育部门下的一个机构,由老师和学生组成。(画成类图方便理解)

1.4.分解

分而治之,解决复杂性的技巧。这里主要介绍两种分解方式。

(1)算法分解

该部分代表为自顶向下的结构化设计,分解为简单的算法,系统中每个模块代表了某个总体过程的一个主要步骤。举一个简单的例子,邮寄快递时,我们先将物品准备好,找到快递员,填写快递信息,进行邮寄。在这个过程中,我们分成了4个步骤,我们更注重的是事件的顺序,而非主要关注参与者

(2)面向对象分解

这是另一种分解事物的方式。根据问题域中的关键抽象概念对系统进行分解。针对上面的快递邮寄的例子,采用面向对象分解时,我们分解成4个对象:物品,快递单,快递员,我。我拥有物品,然后向快递员发出请求,快递员给我快递单让我填写快递信息。然后快递员进行邮递。

(3)算法分解 VS 面向对象分解

由于两种分解方式的存在,在设计发杂系统的时候,我们就需要权衡哪种分解方式更好。事实上,算法观点强调事件的顺序,面向对象的观点强调了一些代理,它们要么发出动作,要么作为其他操作动作的承担者。

2.对象模型

对象模型包括:抽象、封装、模块化、层次结构、类型、并发和持久等原则。

面向对象的分析与设计与结构化方法是不同的:它要求以一种不同的方式来思考分解

2.1.对象模型基础

结构化的设计方法指导开发者利用算法作为基本构建块来构建复杂系统。类似的,面向对象的设计方法利用类和对象作为基本构建块

(1)面向对象编程(OOP)

本书的定义:“面向对象编程是一种实现的方法,在这种方法中,程序被组织成许多组相互协作的对象,每个对象代表某个类的一个实例,而类则属于一个通过继承关系形成的层次结构。

定义的3个要点:

  • 对象是面向对象编程的基本逻辑构建块。
  • 每个对象都是某个类的一个实例。
  • 类类之间可以通过继承关系联系在一起。

(2)面向对象设计(OOD)

本书的定义:"面向对象设计是一种设计方法,包括面向对象的分解过程和一种表示法,这种表示法用于展现被设计系统的逻辑模型和物理模型、静态模型和动态模型。"

(3)面向对象分析(OOA)

"面向对象是一种分析方法,这种方法利用从问题域的词汇表中找到类和对象来分析需求"

(4)OOA ,OOD,OOP 三者的关系

面向对象分析的结果可作为开始面向对象设计的模型,面向对象设计的结果可作为蓝图。最终利用面向对象编程实现具体的系统。

2.3.对象模型要素

先说一说编程风格,Bobrow将编程风格定义为“一种组织程序的方式,基于某种编程概念模型和一种适合的语言,其目的是使得用这种风格编写的程序很清晰”。每一种编程风格都有自己所擅长处理的事情。面向对象风格适用范围相对较广泛,这种编程风格通常作为架构框架,被其他编程风格所使用。

(其他编程风格包括:面向过程,面向逻辑,面向规则,面向约束)

对象模型的4个主要要素:

  • 抽象;
  • 封装;
  • 模块化;
  • 层次结构;

3个次要要素:

  • 类型
  • 持久
  • 并发

(1)抽象的意义

抽象是我们人类处理复杂性的基本方式。下面是不同学者对于抽象的理解:

Dahl,Dijkstra,和Hoare指出:“抽象来自于对真实世界中特定对象、场景或处理的相似性认知,并决定关注这些相似性而忽略不同之处”。

Shaw对于抽象的定义:"对一个系统的一种简单的描述或指称,强调系统的某些细节或属性同时抑制另一些细节或属性。好的抽象强调了对读者或者用户重要的细节,抑制了那些至少是暂时的非本质细节或枝节"

Berzins,Gray,Naumann建议:"只有当一个概念可以独立于最终使用和实现它的机制来描述、理解和分析的时候,我们才说这个概念是抽象的"

本书对于抽象的定义:"抽象描述了一个对象的基本特征,可以将这个对象与所有其他类型的对象区分开来,因此提供了清晰定义的概念边界,它与观察者的视角有关。"

抽象关注一个对象的外部视图,所以可以用来分离对象的基本行为和它的实现。通过“最少承诺”原则达成。正是根据这个原则,对象的接口只提供它的基本行为,不提供其他内容(比如说实现)。另一个原则最少惊奇”原则,这个原则指抽象捕捉了某个对象的全部行为,不多也不少,并且不提供抽象之外的惊奇效果或副作用

所有的抽象都有静态和动态的属性。比如说一个文件有名字,名字作为属性是静态的,这些属性的具体取值则是动态的。

(2)封装的意义

对象的抽象应该优先于它的实现。当选择了一种实现之后,它应该作为这种抽线跟后面的秘密,对绝大多数客户隐藏

抽象和封装式互补的概念抽象关注对象可以观察到的行为,封装关注这种行为的实现。封装通过信息隐藏实现,信息隐藏是将那些不涉及对象本质特征的秘密都隐藏起来的过程。通常,对象的结构是隐藏的,方法的实现也是隐藏的。

复杂系统的每一部分,都不应该依赖于其他部分的内部细节”。

要让抽象能工作,必须将实现封装起来。” 意味着,每个类包含两部分:一个接口,一个实现。类的接口描述了它的外部视图。而类的实现包括抽象的表示以及实现期望行为的机制。通过接口我们可以知道,客户可以对这个类的所有实例做出哪些假定。本书对于封装的定义如下:

"封装是一个过程,它分隔构成抽象的结构和行为的元素。封装的作用是分离抽象的概念接口及其实现。"

举个简单的例子,有一个电灯,其抽象操作有打开,关闭。这两个操作是我们外界可以了解的,可以认为是接口。但是要支持打开,关闭这两种行为,其内部一定需要设计电路等(实现)。 我们作为使用电灯的人,不需要知道其内部原理。

(3)模块化的意义

面向对象中所说的模块化,更多是从物理层面来说的(物理打包)。模块化有助于程序员理解程序。相关例子,java中包的概念,将相似的,服务于同一功能的类组织到一起

将一个程序分割到一些不同的组件中,这可以在某种程度上减少它的复杂性.....虽然从这一点来说,分割程序是有帮助的,但是分割程序的更大理由是它在程序内部创造了一些定义良好的、有文档描述的边界。这些边界,或者叫接口,对于理解程序是非常有价值的。

与结构化设计相比。在传统的结构化设计中,模块化考虑对子程序进行有意义的分组,利用耦合和内聚作为判断的依据。在面向对象中,我们的任务决定了类和对象的物理打包

建议发现正确的类和对象,然后将它们放到不同的模块中先逻辑设计,再物理设计)。类和对象的确定 是逻辑设计的一部分。模块的确定 是系统物理设计的一部分。

PS【举例】 : A调用B多,调用C少。那么我就会尝试把A和B封装到同一个模块中(使其物理位置上相近),而将C放入到另一个模块中。

(4)层次结构的意义

一组抽象常常构成一个层次结构,通过在设计中确定这些层次结构,可以极大简化对于系统的理解在复杂系统中,最重要的两种层次结构是它的类结构(“是一种”层次结构)和对象结构(“组成部分”层次结构)。

(1)”是一种“("is a")层次结构。举例,人是动物。在面向对象中表现为继承

(2)组成(”part of“)层次结构。举例,人由手、脚等组成。在面向对象中表现为组合

练习:选择一个熟悉的事物,通过上面两种方式将其分解为层次结构。

举例,学校,是教育部门下的一个机构,由老师和学生组成。(画成类图方便理解)

待续...(类型,并发,持久化)

编辑于 2019-12-15 11:13