现在的位置: 首页 > 综合 > 正文

从系统论的角度看待软件设计开发模式

2013年12月03日 ⁄ 综合 ⁄ 共 5211字 ⁄ 字号 评论关闭
发信人: dych (一万年太久), 信区: SoftEng
标  题: 从系统论的角度看待软件设计开发模式(全)
发信站: BBS 水木清华站 (Tue Dec 24 07:58:45 2002)

从系统论的角度看待软件设计开发模式

学习软件开发和程序设计十几年,看过不少书籍和讨论文章,但总给人一种隔靴挠痒,未
得要领之感。本文试图换一个角度来探讨软件设计开发模式问题,希冀对初学者和有一定
经验的同行们能有所裨益。若果能起到抛砖引玉的作用,余倍感欣慰矣。
系统的结构越精巧,其运行效率也越高,稳健性也越差,实现起来也越困难。
软件设计开发过程中遇到的几个主要矛盾:
1。系统的功能分解与结构分解的矛盾。
2。系统的静态结构(空间结构)与动态结构(空时结构)的矛盾。
3。系统的全局性与人的视野局部性的矛盾。

开发软件的目的是为了实现功能,满足需要。通常在一个软件中有许多功能需要同时得到
满足,而实现起来又不能一蹴而就,所以就有了功能分解的必要。此所谓分而治之(Divi
de and Conquer)。功能分解是属于设计描述层面的,不是实现层面的。本文强调的结构
分解才属于实现层面,而且通过结构分解把描述与实现联系起来了。从描述层面向实现层
面跨越需要通过解决功能分解与结构分解的矛盾来实现。功能分解与结构分解并不是一一
对应的,因为一种功能往往可以通过多种不同的结构来实现,而一种结构也可以实现多种
不同的功能。我们希望寻找到功能与结构之间最自然的对应关系。
熟悉计算模型的人,(若不熟悉,推荐你看Sipser的《计算理论导引》)会知道计算的基
本单位是图灵机(TM),每个图灵机有一个入口和一个出口,图灵机处理字符串,通过其
内部一系列的状态转换,将输入的字符串变换成输出的通常是不同的字符串。图灵机是最
基本的完整的计算单元。每个图灵机既是结构单元,又是实现确定功能的实现单元,是我
们寻找的功能分解与结构分解的交叉点,是理想的原子单元。按照我们的观点,几条C语言
语句和C++类都不是图灵机,因为它们不满足图灵机有且只有一个入口和一个出口的要求。
而大家熟悉的许多函数,子程序以及完整的C语言及任何高级语言应用程序都可看成是图灵
机。一台计算机可看作是通用图灵机(UTM),它可以模拟其它任何图灵机的运行。我们把
这种基本的完整的计算单元称作组件或模块(Component/Unit)。注意每个组件是一个独
立的个体,通过其唯一的入口和出口与外界交换信息。组件可大可小,大到可以是整个软
件,整个系统,小到可以是由几个语句组成的一个函数。组件实现了功能的封装,它拥有
一个名字以及说明它所实现功能的描述。组件起到了联系功能分解与结构分解的桥梁作用

面向对象程序设计的类其本质是要实现封装和重用两大功能。封装就是分成内外两部分,
隔离开来,做到内外有别。内部的变量和操作对外部不可见,外部只能通过暴露在外的接
口与内部通讯。类的封装并不彻底,因为外部可以直接调用其公共变量和公共函数。这可
看作是为了使用的灵活方便而牺牲了概念的完整性的一个典型例子。再看重用。通常类是
通过继承机制来实现重用的。笔者认为继承并不是实现重用的一种自然的机制,更不是唯
一的机制。很多情况下,只是为了重用类的一小部分代码,硬是使用继承机制把两个类说
成是父子类的类属关系,显得十分牵强而且笨拙。滥用类的继承机制的后果也是可怕的。
想一想VC的MFC类库和Java类库那些庞杂而笨重的继承关系,你感到的不是清晰,更多的是
一种混乱,牵强附会,破绽百出。何况类的继承机制过于灵活,即使很有经验的程序员也
难以恰如其分地理解和运用,不同的程序员使用起来风格迥异,相互之间难于理解和沟通

面向对象程序设计中类的概念,由于其封装的不彻底,继承式重用机制的缺陷使我们考虑
放弃使用类的概念。那我们怎样实现代码重用呢?我们可以使用组件组装机制和框架来实
现重用。
按照前面的定义,一个组件就是一台图灵机,具有唯一的入口和出口。组件可大可小。一
个较大的组件可以由多个较小的组件组装而成,我们说大组件包含小组件。举个简单的例
子。组件A实现将两个数相加的加法功能,组件B实现将两个数相乘的乘法功能,组件C包含
了(或称组合了)组件A和组件B,可以实现加法和乘法两项功能。再举一个例子。组件A对
一个由数字0/1组成的串取反,即将所有的0改写成1,将所有的1改写成0。组件B将一个0/
1串取逆,即反向写这个串。组件C包含组件A和组件B,可以实现功能A和功能B的复合,即
按顺序先实现功能A(取反),然后实现功能B(取逆)。这样一来,组件C就对一个0/1串
实现了先取反再取逆的功能。注意这两个例子差别的细微之处:前者为功能的组合,后者
为功能的复合。因为组件都有唯一的入口和出口,要实现组件组合或复合的功能是比较容
易的。对于第一个例子,组合组件只须从其入口参数中知道外界要求的是加法还是乘法,
即可方便地转向调用相应的子组件。对于第二个例子,复合组件可以使用管道技术,将一
个子组件的输出结果定向到另一个子组件的入口来实现功能的复合。当然还可以使用其它
实现技术,我们这里主要关注概念,而非具体实现。
一种更高级的重用机制是框架重用。框架(Framework)是由槽或称空穴(Slot /Cavity/
Hole)通过管道或称通道(Pipeline/Channel)按照一定的顺序连接起来构成的实体。框
架中槽的概念类似于面向对象程序设计中的虚类,起着占位的作用,是原型,是虚拟实体
,有待于框架在使用过程中与特定的组件结合而实现实体化。但槽的接口是规定好了的,
槽与槽之间通过管道相连接。这种连接是静态连接,区别于后面将要谈到的动态连接。一
个槽可以与多个其它的槽相连接。每个槽在实体化过程中都要与一个特定的组件相结合,
结合了组件的槽称为角色。当一个框架中的所有的槽都结合了相应的组件之后,这样的框
架称为满的。在此之前的框架是不满的或是空的。因为同样的组件可以结合到不同的框架
中的不同槽上,从而充当不同的角色,这就是组件的多态性。一个框架形成了特定的上下
文(Context),同样的组件在这个上下文中的语义与它在另外的上下文中的语义可能大相
径庭,即组件的角色可以完全不同。这就是所谓一词多义,多态性其实就是一词多义。
这里有一个问题:一个槽可以连接多个其它的槽,而且一个槽必须和一个组件相结合,而
组件只有一个入口和一个出口,这个问题如何解决呢?这涉及静态结构与动态结构的关系
问题。框架中槽之间的连接——管道是属于静态连接,与时间无关,一旦槽结合了组件并
运行起来之后,组件之间的连接就属于动态连接,与时间紧密相关。在每个特定的时刻,
一个组件只跟另外的唯一的组件保持连接,接受其输入输出。这有点像操作系统的分时概
念,从一段时间看,操作系统不间断地运行了多个不同的进程,但在每一个特定的时刻,
它只是在运行唯一的一个进程,操作系统是在多个进程间不停地切换。框架也是一样。在
运行时,框架的管道好似一个个开关,不停地切换到需要进行通讯的嵌在框架的槽上的组
件之间,为它们建立实时的连接通道。这就是框架的运行时结构,即动态结构,可以看到
它是十分复杂的。
我们之所以要求组件只有一个入口和出口,而不是像传统上那样允许组件有多个参数,主
要是因为我们设想框架和组件是独立开发的,框架中只具有比较原始的槽,它的主要作用
是构造好槽之间的连接——管道,这属于基础设施建设。在结合组件之前,框架根本不知
道组件的接口,甚至就连将要结合什么组件也是未知的。所以只能要求组件具有一致的接
口,我们这里使用唯一的入口与出口来实现,也就是要求组件的输入和输出都是一个字符
串,退化的情形下可以是空串。至于字符串的内容如何解释,比如一个输入串可能打包了
多个必需的参数,那是属于语义问题,由组件负责处理,框架只是负责将这些字符串从一
个槽传递到另一个槽,这经由管道完成。而组件具有唯一的入口与出口也保证了组件的独
立开发与测试能力,在与框架的槽结合时容易做到即插即用。
框架和组件都是可定制的,可重用的实体,它们可以分别地独立地开发和调试。其实,框
架本身可设计成组件的形式,也具有一个入口和一个出口,这样就可作为一个完整的组件
嵌套到更大的框架内使用。一个系统可以由若干个大的框架组成,大框架下嵌套着小框架
,诸如此类,形成有序的层次结构,框架的槽上结合着组件。总之,系统就是通过框架把
组件结合在一起形成的有机整体,这也符合人对自然的直观认知。
以上是对框架的粗略的描述,实现起来需要解决一些技术细节上的问题。比如连接框架中
各槽之间的管道如何实现的问题,可以使用消息传递机制,而且要考虑到性能,容错,同
步死锁,实现的复杂程度等等,需要专门的篇幅讨论,这里就略去了。
上面涉及到的主要是系统的功能分解与结构分解的问题,下面简要提及另外两个矛盾。
我们知道,凡是具有一定规模的较为复杂的系统,都不是少数几个人能够完成的,这就涉
及到多人协作的问题。人数越多,花在人与人之间交流和沟通上的时间与精力就越多,相
比于人数较少时,每个人的平均效率是下降了。这好比是一个集合,分解成若干个较小的
子集合,这些子集合的并要覆盖原来的集合,但子集合之间互有重叠。分解的越细致,子
集合的数量越多,相互之间的重叠就越多,最后所有子集合的面积加起来,可能会达到原
来集合的若干倍。软件开发中多人合作的情形与此类似。当有上百人合作一个项目时,大
量的工作花在了组织协调上面,总的工作量可能多花了好几倍。在一个大系统中,很多人
只是负责系统的某个很小的部分,以致于对系统的全局没有什么概念。这就是前面提到的
系统的全局性与个人的视野局部性的矛盾。即便对系统的总体设计师也有一个全局和局部
的关系如何把握的问题。心理学研究表明,个人能够同时处理的任务数大致是7±2个。也
就是说,人的视野是有限的,难以同时顾及10个以上的个体,只能顾及到视野范围内很少
的几个个体,其它的就视而不见了。但是人作为高级动物具有抽象能力,能够从高处看问
题,所谓站得高看得远。将系统分解成层层嵌套的层次结构(Hierarchy)正是运用了这种
抽象能力解决系统的全局性与视野有限性矛盾的方法。框架式结构分解方法就是应用了层
次结构分解。
以上谈到的功能分解与结构分解以及全局结构与局部结构都是属于静态结构的部分,是空
间结构的层面,而系统运行时的结构还要把时间维度加进来,是时空结构,属于动态结构
的层面。实际上,系统的动态结构要复杂得多。从宏观上看,信息从系统的一部分流向另
一部分,更具体地说,是数据流和指令流从一个框架流向另外的框架,在框架内部是从一
个组件流向其他的组件,其中有很多数据与指令的流动是随机触发的,事先难以预测。当
系统的复杂程度达到一定规模时,甚至会产生混沌(Chaos)现象。混沌现象的出现从某种
程度上预示了人类认识世界和改造世界能力的局限性。因为混沌是对初始条件极端敏感和
不可预测的代名词,它反映的是客观世界的无限复杂与人类认知的有限至多是可列无穷之
间的矛盾。实际上,人类是根据自身感知器官获得的有限数据建构起整个世界模型的,可
以想象且完全符合逻辑的是存在另外的生命形式,他们的感知器官与我们的并不一致,他
们能够感知到我们无法感知的东西,那么他们所理解和建构的世界模型就有可能与我们的
世界大相径庭。现代物理学中的超弦理论预示了多维时空存在的现实可能性,注意这里的
多维时空并非只是传统上数学家脑子里构造的抽象概念,而是现实生活中的客观存在。那
么我们完全有理由设想存在那样的生物,他们能够感知更多维的时空,他们感知的世界景
象我们永远也感受不到。甚至于他们与我们生活在完全正交的时空中,彼此无法感知对方
的存在。这并非无稽之谈,而是完全可能的。
以上的讨论似乎有点玄学的味道,但是“玄之又玄,众妙之门”。一门科学,如果不能够
上升到哲学的高度,那它就只是停留在工匠的层次,就算是工匠也不会是好的工匠,无法
实现超越自身,做到融会贯通。
说到底,所有的程序设计和软件开发模式都是面向人的,是为了方便人理解和沟通的,因
为对于机器来说,所有的程序都要编译成机器代码,就是机器认识的01串。我们寻找的是
那种面向人的,简单的,直观的,便于人理解和交流的程序设计和软件开发模式,基于框
架和组件的模式无疑是一个好的尝试,作为面向对象模式的改进和发展,现在也已开始流
行。当然,这种基于框架和组件的模式仍显得过于机械和呆板,不够智能化,没有把人工
神经网络等经验和成果包括进来。可以预期,下一代的程序设计和软件开发模式将会应用
神经网络,遗传算法,机器学习理论等等研究成果,使软件的设计开发更加智能化,自动
化,从而加快实现程序完全自动生成的一天早日到来。

--

※ 修改:·dych 於 Dec 24 08:02:28 2002 修改本文·[FROM: 141.225.225.136]
※ 来源:·BBS 水木清华站 smth.org·[FROM: 141.225.225.136]

抱歉!评论已关闭.