【课程基本信息】
- 课程名称:《软件体系结构》
- 开课单位:浙江科技学院信息与电子工程学院(现浙江科技大学计算机科学与技术学院、人工智能与信息工程学院)
【图书基本信息】
- 出版社:清华大学出版社
- 书名:《软件架构设计实践教程》(封面见目录之下)
- 编著:张家浩
- 出版年份:2014年
【文章基本信息】
- V1.0:2021年5月,Word 版本定稿
- V2.0:2026年3月1日,Markdown 版本公开发布
【相关文章】
- 小论文:视频类系统架构演进路线综述及战术分析
- 实验:
- 实验一:负载均衡实验报告
- 实验二:ACME+SOA 实验报告
- 实验三:软件架构实战实验报告
文章目录

前言
教学目的
- 这是一本与传统《软件体系结构》教科书不同的教材。作者在此明确提出:课程的培养目标,是为软件企业培养未来的架构师,而不是为了迎合少部分学生“考研、读博”,虽然两个“目标”可能可以“和谐”相处。
- 培养未来10年后的架构师,现在需要让他知道什么?作者认为,与软件项目管理、软件工程等(甚至包括组织行为学等)课程一样,开设这些课程的根本目的;不在于教会学生多少具体的相关概念和技术方法,而是从现在开始,培养一种面向软件工程高端(与编程相比)角色(项目经理、架构师、需求分析师等)的意识和觉悟,即进行软件架构的“启蒙”教育、“目录”教育。
- 每章最后都安排有一两个实践小项目并配有相应的基础代码(在出版社网站上可以下载)和思考题,大多数都是围绕本章主题的体验性程序,且以“二次开发”项目为主,以加深对本章内容的认识和理解。
第一章 认识软件架构
1.1 软件架构与软件工程
1.1.1 软件产业的工业化与现代化
- 人们用计算机技术改造传统产业的同时,软件产业之外的人,几乎想象不到,软件产业自身往往还处在“刀耕火种”的时代。
1.1.2 软件系统的复杂性

实际上,一个软件系统的复杂性因素,可包括功能、性能、可用性、安全性与稳固性、容错性、灵活性与可扩展型、兼容性与可移植性、开发和维护成本等。算法的好坏、数据结构的优劣、存储空间的大小,甚至网络的带宽,已经不是问题。 上述因素,全部或部分都与系统的架构有关。
1.1.3 克服“软件危机”的进程
- 进入90年代以后,软件工程的一个重要进展,就是基于构件的开发。有人把基于构件的软件开发,称为第四代软件工程,或者说是“现代软件工程”的开始。
1.1.4 现代软件产业发展的时代特征
1.一般产业阶段的划分与阶段特征
- 一个产业通常都可以分为婴儿期、青少年期、壮年期和老年期4个阶段。
- 在产业的婴儿期阶段,整个产业还没有真正形成,仅以某一形式与其母体产业结合在一起,并慢慢从母体产业中分离出来(或胎死腹中)。这个阶段的主要矛盾是新概念的有与无。
- 在产业的青少年期阶段,产业开始形成规模,技术日新月异,公司如雨后春笋般出现,产业经营处于暴利阶段。这个阶段的主要矛盾是有没有新技术。 谁掌握了新技术,谁就将获得“垄断”地位,从而获得“暴利”。
- 第三个阶段为壮年期。在该阶段,产业所需技术已基本成熟,分主越来越细,产业进入稳定持续发展期。在这个阶段,产业链已经形成,公司经互相兼并在产业链中都已找到了相对稳定的定位。因为技术不再是某几个公司的专利,因而,这个阶段的主要矛盾是谁将稳定地掌握“新工艺"——只有通过工艺的不断创新,才能降低成本、提高质量,才能在“价格战”中取胜,获得长期稳定的发展。
- 第四个阶段是老年期,俗称“夕阳工业”。在这个阶段,技术已经非常成熟,工艺也已无太大的改进空间,于是劳动力成本上升成为主要矛盾,新的替代技术和产业在这个阶段的后期已经开始慢慢地萌芽,最终取而代之。
- 当前全球的软件产业正处于壮年期,它期待通过软件开发工艺的创新,来获得长期稳定的发展。
-
软件产业的阶段特点
-
软件生产流程与工艺的特征
软件产业的工艺与传统产业的工艺并没有本质的区别,它同样由生产过程、原料、零件配送流程、操作参数优化、公用工程等几个主要方面构成。
-
软件业需要工艺革新,首要的任务就是改变现有的软件生产方式。要采用流水线自动化的生产方式,并且要如同传统产业一样工厂化的管理。
-
必须优化应用软件的质量,而应用软件的优化,将体现在两方面:
- 一方面是作为应用软件业务组成的唯一原材料——业务对象与业务对象间关系的优化。这就是业务构件的划分大小的优化。如果业务构件大了,灵活性就小了;如果业务构件粒度过小了,其耦合度和组装成本就高了。所以,必须找到一个最佳点。
- 另一方面是优化客户需求中的业务流程。
-
要工艺革新,就必须解决原材料的来源问题。应用软件的唯一原材料就是业务构件。所以,必须能够完成业务构件的自动生成、自动配送。
-
同理,在应用软件里,有非常多的资源是可以公用的。
- 比如处理操作系统间关系的支撑体系、处理和数据库间高效交互的数据服务休系,都是可公用的资源,这些资源,也就构成今天所提的“中间件”的概念。
- 应用软件是企业管理活动的映射,在一定意义上说,它就是虚拟的企业。
- 企业的管理活动一直在变,但是,随着它成熟度的增加,已经有一部分管理活动变得越来越规范,比如企业组织架构等。
- 这些资源,在任何一个应用软件中基本都是同质的,也就是说,可以形式化成为业务支撑体系,即业务通用平台进行公用。
- 以上种种资源都不应该由应用软件集成商去完成其生产,必须由专业的厂商来完成,这样才能够真正降低应用软件生产的成本。
-
-
软件工艺改进的方向
- 北美软件业正在将许多老产品的服务与二次开发转向印度等海外市场,其实质是北美软件业正寻求和酝酿着一场新的革命。这一革命,肯定会在最短的时间内转向以软件开发自动化和业务构件灵活组装为核心的软件开发工艺革命。
1.1.5 国内软件产业发展的问题
- 克服软件危机,国外取得了很大的进步,但国内进展缓慢。
- 组织的软件过程成熟度水平,与企业所处的生存环境和经济条件、规模、盈利能力有关,这些基本要素决定了企业在软件过程成熟度方面提高的力度。
- 中国的老板们,乐于、忙于应酬的多,研究和关注技术的少。虽然技术工作可以委托他人,不必自己亲力亲为,但是,同时站在管理和技术两个方面,进行规划、推动、控制和检查的人,只能是老板自己,无人可以取代。 缺了这个角色,组织的过程控制,只能玩些虚招,混个招牌而已。
1.2 软件架构概述
1.2.1 软件架构的定义
-
目前所讨论的对象,在传统教科书中,一般称为“软件体系结构”,而业界则称为“软件架构”,它们都是来源自英文 Software Architecture。
-
被各种教科书采纳较多的一个定义是:软件架构是由结构和功能各异、相互作用的构件集合,按照一定的结构方式构成的系统。它包含系统的基础构成单元,它们之间的作用关系,在构成系统时它们的集成方法以及对集成约束的描述等。
-
从软件系统的角度理解架构定义,可以看到:软件的架构是关于软件系统如何被组织起来的定义,即软件系统是由以下三个要素构成的。
1.组成系统的结构元素,或统称为组成系统的构件
- 软件架构定义了组成系统(局部或总体)的构成要素。
- 常见的局部或总体的构件可以是:客户、服务器、数据库、中间件、程序包、过程、子程序、进程等。
- 至于什么是局部,什么是总体,其具体的构成要素是什么,关键看站在系统的什么位置看系统(分解的颗粒度)。
- 构件与构件之间的连接,以及特定的连接关系
- 软件架构定义了这些构件之间的相互作用关系,更明确地说,是构件之间的连接(也有定义称为“连接件”)和连接关系。
- 常有的这些连接与连接关系有:过程调用、共享变量访问、信号灯、进程通信、消息传递、网络协议等。
- 例如,两个进程(构件)之间,通过进程调用实现进程间的连接和协同,进程通信(IPC)是连接件,对IPC内容的理解和约定是连接关系(约束)。
3.系统集成的方法和约束
- 架构比需求更进一步地面向或满足系统非功能性的内容,如容量、数据吞吐量、一致性、兼容性、安全性、可靠性等。
- 架构必须能满足特定关键需求,这是架构最主要的功能之一。
进一步地理解软件构架定义,可以知道:
- 架构是一个或多个机构(子架构、可不断细分)的抽象,是由抽象的构件表示的
- 构件之间相互具有联系
- 相互之间的联系具有某些行为特征(连接关系)
1.2.2 软件架构的视角
-
架构描述在不同阶段,依据层次和细节的不同,视角也不同,一般可分为概略型、需求型和设计型。
-
概略型 是上层宏观的描述,反映系统最上层的构件和连接关系,如通常所说的 C/S、B/S 架构等。
-
需求型 是对概略结构的深入表达,以满足用户功能和非功能需求的表达为主。如可支持不同业务需求的框架结构等,Struts 架构就是典型的针对基于 Web 应用的架构。
-
设计型 从设计实现的角度,对需求型进行更深入的描述表达,需要从不同的侧面/视图,设计系统的各个层面,对各个构件和连接结构进行描述。在这个层面上的描述,将直接为系统实现和性能分析服务。MFC(Microsoft Foundation Classes,微软基础类库,基于C++) 就是典型的面向设计和实现的架构。
在不同阶段,架构发挥着不同的作用。
- 在项目规划阶段:架构是项目可行性、工程复杂度、计划进度、投资规模和风险预测的依据(概略型)。
- 在需求分析阶段:架构是开发团队与用户进行需求交互沟通的表达形式和结果(需求型)。
- 在系统设计阶段:架构是系统设计分解和实现、验证的基础。
- 在项目实施阶段:架构是工作分工、人员安排、组织协调、绩效管理的依据。
- 在项目测试阶段:架构是性能测试和评审的依据。
- 在维护升级阶段:架构是在保证系统整体合理性、正确性、性能和可控的维护成本的前提下,软件系统修改、扩充、升级的基础模型。
1.2.3 软件架构的表示方法
- UML 提供了 5 组 9 张视图来表示架构。这 9 张视图是根据 1.2.2 节所述不同观察者的角度来进行描述的,它关注观察者的关注点,而忽略与关注点无关的部分。
- UML 图举例:部署图、类图。【相关文章】「酒店 × 视觉AI」项目|UML分析报告
1.2.4 架构的一般特性
一、架构的风格和层次
1.1 架构风格(风格是一种现实存在的抽象)
- 数据流系统:顺序批处理、管道和过滤器。
- 调用-返回系统:主/子程序。
- 面向对象系统:层次结构。
- 独立构件:通信进程、事件隐式调用。
- 虚拟机:解释器、规则系统。
- 以数据(库)为中心的系统:数据库、超文本、黑板。
- 特殊领域风格:过程控制、模拟器。
- 特殊结构风格:分布式处理、状态转移系统等。
1.2 架构的层次
系统是由许多层次构成的,每个层次处理不同的问题。每个层次都由两个方面组成:构件和规则。
- 构成系统的原始的或集成的构件;
- 由构件集成为系统的集成规则(静态);
- 为系统提供语义的行为规则(动态)。
二、架构发展过程
在软件发展过程中,对架构的认识经历了三个层次的过程。
- 执行级:以针对存储器的映射、数据地址的安排、堆栈和寄存器的分配等为中心。在这个阶段,架构的构件是硬件,集成和行为规则是这些硬件单元的非常具体明确的组合集成方法。
- 代码级:以针对算法和数据结构的选择等为中心。在这个阶段,架构的构件是程序设计语言的字符、指针、进程控制等,集成的是记录、数组和过程等。集成和行为规则是以程序设计方法、数据结构方法为核心。
- 架构级:以针对与构件相关联的系统总体性能为中心。在这个阶段,构件是模块、模块的相互关系等,它重点研究从模块到子系统、到系统的集成规则。
三、架构级架构设计的特点
在这个阶段的软件架构的集成规则和行为规则,则主要关注:
- 通过架构所表现的关于软件的系统级层次上的组成和行为模式;
- 架构表达了软件构件和构件之间的联系;
- 构件的描述应包括:构件功能、结构特征、非功能特征。
- 在这里,构件的概念(也常常被称为“组件”,二者的含义有些微妙的不同)根据设计方法的不同,代表不同的对象,如:类/对象、库/包、构件/组件、制品/配置项/基线等。
- 它们的集成度提高了,同时,它们也不再只是软件制品本身,也出现了针对软件开发过程和为开发管理需要而产生的管理制品,如基线、配置项等。
1.2.5 统一过程(RUP)的架构
- 统一过程(RUP)最核心的特点(三大要素)是:用例驱动、迭代开发、以架构为中心。
- 用例驱动和迭代开发可以很好地适应需求变更的需要。
- 而迭代开发是以架构为基础的增量开发。
1.3 感受身边的架构存在
1.3.2 鼠标接口的架构
用架构定义三要素的模式看,鼠标无疑是计算机的一个构件,用连接线与计算机连接在一起,其连接关系就是鼠标与主机板的接口标准。
1.4 两个小程序的架构分析
1.4.1 两个小程序
- 运行“欢迎”程序,当使用者单击菜单栏上的“欢迎”按钮时,程序会弹出一个对话框,并显示一句欢迎词,单击“确定”按钮可以关闭对话框。而“计算器”程序则根据使用者单击的不同按钮,完成简单的计算器功能,并显示计算结果。
- “计算器”多做的工作,不是在接收到按钮消息后,简单地显示一条“欢迎”信息,而是实现“根据用户单击的按钮和四则运算规则计算并显示结果”。
- 看一下代码就可以知道,从程序实现的角度看,后者与前者相比,不但逻辑复杂度和编程量差别并不大,更重要的是其架构基本相同。
1.4.2 “欢迎” 程序的实现过程
第一步:创建项目
- MFC 为创建应用程序提供了各种模板,模板首先为程序员减少了花在界面设计和实现上的时间。这种方式是几乎所有开发平台(例如,苹果、安卓等)所必需。显然,选择不同的应用程序模板,其内部预制的架构也是不同的,在这里,MFC对应用程序进行了第一次分类。
- 在本小节的剩余部分,笔者将使用 Java Swing 作为示例进行简单讲解,并掺杂图书中的部分MFC 相关原文,读者简单浏览即可。
- 在 IntelliJ IDEA 中,新建一个 Java 项目,使项目内的模块与项目同名。
第二步:创建窗口
- 右键单击模块下的src文件夹>New>Swing UI Designer>GUI Form,输入窗口名
MenuAcce。
第三步:完成程序
第四步:添加按钮
-
在右侧的Palette工具窗口中,选中JButton,拖动到中间的JPanel上。在左侧的Component Tree工具窗口中,选中刚添加的JButton,将它的text属性设置为“欢迎”(或双击按钮直接设置)。
-
从程序设计角度看,这一步是创建了一个行为触发器——按钮,而从架构设计和实现角度看,这里创建了一个新的构件——“欢迎”按钮。这个构件是“欢迎”程序的核心构件(当然不是唯一构件)。
第五步:创建消息映射
- 在JPanel中右键单击“欢迎”按钮>Create Listener>ActionListener。在弹出的窗口中选择要实现的方法:
actionPerformed。 - IDEA通过这种方式,建立了按钮(构件,本质上也是代码)与监听器的处理方法构件(代码)之间的连接。实际上,IDEA是通过自动在对应的 MenuAcce.java 文件中,给MenuAcce类添加一个ActionListener来实现连接的。
【相关原文】
- MFC 通过某种方式,建立了按钮(构件,本质上也是代码)与处理函数构件(代码)之间的连接。
- 实际上,MFC 是通过自动在
MenuAcceView.h文件中,为按钮消息处理函数OnMenuacce()定义一段代码来实现连接的。 - 这一步的意义,是让 Windows 知道,当按钮被单击的时候,“消息”发给谁,找谁来处理这个按钮事件。
- 从架构角度看,这就是构成架构的第二个要素——建立构件之间的“连接”。
- Windows 的消息机制,是 Windows 应用中,按钮构件与按钮处理函数构件之间建立联系的“连接件”。
- 在所有 Windows 系统下的软件,大部分都是靠这个机制进行联系的,而这样建立连接的“操作”,如改用 VS2010,则与 VC++6.0 相比,已经被越来越深地“封装”起来,一般用户看不见,也无须知道了。
- 例如,在苹果的 iOS 或安卓等平台上,这样的关联,只需要在 IDE 上,用关联线“勾”一下就可以了。
- 注意:编程的可视化,就是高度抽象、屏蔽细节,最终形成如视窗、控件这样的可视化的工具。以后人们只需要关注两头:控件使用(用户界面设计)、处理函数(具体业务处理逻辑)。
第六步:编写处理函数
- 应用程序到了这一步,可能还需要程序员写一些代码,但架构师的任务已经基本最后一步,从架构的角度讲,是完成另一个构件——“处理函数"。
- 同时,在处理函数模块中,实现架构概念的第三个要素:构件之间的“交互协议”,交互协议的具体内容,应体现在按钮的内容、传递的消息内容和对应的处理函数三者之中。
第七步:完成程序(补充)
将JPanel命名为panel1,在MenuAcce类中按Alt+Insert>Form main(),IDEA 将自动生成main方法。现在可以编译运行这个程序了,参考代码如下。
MenuAcce.java
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MenuAcce{
private JButton 欢迎Button;
private JPanel panel1;
public static void main(String[] args) {
JFrame frame = new JFrame("MenuAcce");
frame.setContentPane(new MenuAcce().panel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public MenuAcce() {
欢迎Button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("欢迎!");
}
});
}
}
MenuAcce.form
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="MenuAcce">
<grid id="27dc6" binding="panel1" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="54ed6" class="javax.swing.JButton" binding="欢迎Button" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="欢迎"/>
</properties>
</component>
</children>
</grid>
</form>
1.4.3 小程序的架构实现小结
- 创建新按钮是创建新的构件;
- 建立消息映射是建立构件之间的连接;
- 处理函数则根据对按钮、消息、处理三者之间的“协议”,编写相应代码。
1.5 实践与思考
1.5.1 实践题
Q1:把“欢迎”按钮改成“时间”按钮,并显示当前时间。
A1:更改“欢迎”按钮的text属性值为“时间”,更改处理函数。
Q2:在有“欢迎”和“时间”两个按钮的界面上,把“欢迎”按钮和“时间”按钮各自对应的“效果”对调一下,使得单击“欢迎”,显示的是时间;单击“时间”显示的是“欢迎”。
A2:
-
右键单击“欢迎”按钮>Duplicate。
-
要对调效果,只需对调两个按钮的
actionPerformed处理方法中的内容即可。
1.5.2 思考题
Q4:从架构三要素的角度,说明“欢迎”程序的架构与“计算器”程序的架构有什么相同和不同?
A4:
-
不同:各构件(按钮、处理方法)不同、连接不同。
-
相同:基本上是一个按钮连接一个处理方法。
Q5:在实践题2中,老师希望同学们在“欢迎”程序的基础上,再实现一个“时间”按钮,并把“欢迎”与“时间”按钮的实际效果“对调”一下,有同学就把两个相应的处理函数中的代码对调了一下,更有同学把按钮的名字对调了一下,这是非常错误的。请从架构师的角度仔细想想为什么错?应该如何实现“对调”?
A5:对调实际效果,实际上是对调按钮构件与处理方法构件之间的连接关系,而不应对调构件中的内容。(上面的actionPerformed处理方法并不是独立的构件)


1074

被折叠的 条评论
为什么被折叠?



