2.2 概念思想的解析
2.2.1 软件架构关注分割与交互
架构设计是分与合的艺术。
“软件系统的架构将系统描述为计算组件及组件之间的交互”,Shaw的这个定义从“软件组成”角度解析了软件架构的要素:组件及组件之间的交互。如图2-1所示(采用UML类图),架构=组件+交互,组件和组件之间有交互关系(图中的“交互”关系建模成UML关联类)。
图2-1 软件架构的要素:组件及组件之间的交互
下面以大家熟悉的MVC架构为例进行说明。如图2-2所示。
图2-2 MVC架构作为“组件+交互”的例子
· 采用MVC架构的软件包含了这样3种组件:Model、View、Controller。
· 这3种组件通过交互来协作:View创建Controller后,Controller根据用户交互调用Model的相应服务,而Model会将自身的改变通知View,View则会读取Model的信息以更新自身。
通过此例可以看出,“组件+交互”可以将MVC等“具体架构设计决策”高屋建瓴地抽象地表达出来。
2.2.2 软件架构是一系列有层次的决策
架构属于设计,但并非所有设计都属于架构。架构涉及的决策,往往对整体质量、并行开发、适应变化等方面有着重大影响(否则就放到详细设计环节了):
· 模块如何划分。
· 每个模块的职责为何。
· 每个模块的接口如何定义。
· 模块间采用何种交互机制。
· 开发技术如何选型。
· 如何满足约束和质量属性的需求。
· 如何适应可能发生的变化。……
还有很多
而且实际的设计往往是分层次依次展开的——无论是决策如何切分系统还是决策技术选型都是如此:
· 例如,你设计一个C/S系统时,是不是经历着这样一个“决策树”过程:……嗯,我决定采用C/S架构,系统包含Client和Server;……嗯,我决定将Server分为三层;……嗯,我决定将Server的引擎层划分为N个模块;……(如图2-3所示。)
图2-3 架构设计过程是一棵决策树(切分类决策)
· 例如,你设计一个B/S系统时,可曾有过这样的“决策过程”:……嗯,我决定B/S前端采用JSP技术;……嗯,具体到Framework我选Struts;……(如图2-4所示。)
图2-4 架构设计过程是一棵决策树(技术选型类决策)
再举一例。现在你来设计一个硬件设备调试系统。
第1步,理解需求——此时软件系统是黑盒子
如图2-5所示,你要设计的系统现在还未切分,它的主要需求目标有:
图2-5 设备调试系统:主要目标(CRC卡)
· 作为设备调试系统,其主要功能是实时显示设备状态,以及支持用户发送调试命令;
· 另外,由于是为硬件产品配套的软件系统,所以它必须容易被测试,否则是硬件故障还是软件故障将很难区分;
· 再就是必须具有很高的性能,具体性能指标为“每秒钟能够刷新5次设备状态的显示,并同时支持一个完整命令字的发送”。
第2步,首轮决策——此时软件系统被高层切分
之后,软件架构师必须规划整个系统的具体组成。通常,对于一个独立的软件系统而言,它常常被划分为不同的子系统或分系统,每个部分承担相对独立的功能,各部分之间通过特定的交互机制进行协作。
而此例中的设备调试系统则不同,它有两个相对独立的应用组成:一个桌面应用和一个嵌入式应用。
那么,它们如何通信呢?最终决定,将它们通过串口连接,采用RS232协议进行通信。
再接下来,架构师必须决定这两个应用分别担负哪些职责(如图2-6中的CRC卡所示):桌面应用部分负责提供模拟控制台和状态显示;而嵌入式应用部分负责设备的控制和状态数据的读取。
图2-6 设备调试系统:组成部分(CRC卡)
第3~N步,继续决策——此时软件系统被切分成更小单元
……现在,设备调试系统的桌面应用部分,也要划分成模块吧(如图2-7所示)。
图2-7 其中的桌面应用:进一步分解(CRC卡)
通信部分被分离出来作为通信层,它负责在RS232协议之上实现一套专用的“应用协议”:当应用层发送来包含调试指令的协议包时,它会按RS232协议将之传递给嵌入部分;当嵌入部分发送来原始数据时,它将之解释成应用协议包发送给应用层。
而应用层负责设备状态的显示,提供模拟控制台供用户发送调试命令,并使用通信层和嵌入部分进行交互。
……如此步步设计下去,你就该问自己“架构的哪些目标还未达成”诸如此类的问题了。例如“应用层”怎样高速响应用户?例如“通信层”如何高性能地接受串口数据而不造成数据丢失?在此不再赘述。
2.2.3 系统、子系统、框架都可以有架构
虽然我们最常听到的说法是“软件系统的架构”,但未必是完整的软件系统才有架构。真实的软件其实是“由组件递归组合而成”的,图2-8运用Composite模式刻画了这一点:
图2-8 借助Composite模式刻画真实的软件
· 组件的粒度可以很小,也可以很大;任何粒度的组件都可以组合成粒度更大的整体。即所谓的粒度多样性问题;
· 组件粒度的界定,必须在具体的实践上下文中才有意义;你的大粒度组件,对我而言可能是原子组件。即所谓的粒度相对性问题;
· 组件分为原子组件和复合组件两种;在特定的实践上下文中,原子组件是不可再分的;复合组件是由其他组件(既可以是原子组件,又可以是复合组件)组合而成的;无论是原子组件还是复合组件,它们之间都可以通过交互来完成更复杂的功能。
是时候为“软件架构”找准位置了,答案看上去惊人的简单(如图2-9所示):
图2-9 为“软件架构”找准位置
· 架构设计是针对作为复合整体的“复杂软件单元”的,架构规定了“复杂软件单元”如何被设计的重要决策;
· 实际上,系统、子系统和框架(Framework)根据需要都可以进行架构设计。
例如,航空航天领域的系统往往极为复杂,这样一来,总的系统需要配备系统架构师,子系统有时也会分别单独配备架构师。
又例如,用友华表Cell组件是标准的报表处理ActiveX组件,它提供几百个编程接口。虽然在用户看来它只是小小的“开盒即用”的ActiveX组件(上面说的“原子组件”),但它的研发团队从需求分析到架构设计到程序开发……都样样经历(上面说的“复杂软件单元”)。
再例如,随着面向服务架构(Service Oriented Architecture,SOA)被越来越多的人所接受,基于组件的软件工程(Component Based Software Engineering,CBSE)也为更多的人所认识。在此种情况下,整个系统的架构模式是SOA,而每个组件本身也有自己的架构设计——在实践中不了解这一点会很危险。
推广开去,其实任何作为复合整体的复杂事物都可能有架构,比如一本书、一幢建筑物。那本“永不褪色的经典”《如何阅读一本书》中就说:“每一本书的封面之下都有一套自己的骨架(Every book has a skeleton hidden between its boards)。”