【软件构造】第一章
多维软件视图
-按阶段:构建和运行时视图
-动态:时刻和周期视图
-按级别:代码和组件视图
-每个视图的元素、关系和模型
(1)构建时视图
A. 概述:想法 -> 需求 -> 设计 -> 代码 -> 可安装可执行的包
代码级视图:源代码——源代码如何由基本程序块逻辑组织,如函数、类、方法、接口等,以及它们之间的依赖关系
组件级视图:体系结构——源代码是如何通过文件、目录、包、库以及它们之间的依赖关系进行物理组织的
时刻视图:源代码和组件在特定时间内是什么样子的
时间视图:它们是如何随着时间演变/改变的
B. 代码-时刻
三种相互关联的形式:
- 面向词汇的源代码:半结构化的源代码
- 面向语法的程序结构:将半结构化源代码表示为结构化树,例如,抽象语法树(AST)。
- 面向语义的程序结构:例如,类图,使用类图(UML)来描述接口、类、属性、方法以及它们之间的关系,通常在设计阶段建模,并转换成源代码,它是根据用户需求进行面向对象分析和设计的结果。
C. 代码-周期
代码变动:从一个版本到另一个版本的文件中添加、修改或删除的行。描述随时间“变化”的视图。
D. 组件-时刻
源代码被物理地组织成文件,这些文件进一步由目录组织。文件被封装到包中,并且在逻辑上,被封装到组件和子系统中。可重用模块以库的形式存在。
库:
存储在自己的磁盘文件中,收集一组代码函数,可以跨各种程序重用。
开发人员并不总是构建一个单一的可执行程序文件,而是将定制开发的软件和预构建的库加入到一个单一的程序中。
库的来源:
-从OS预装的一套库操作,如文件和网络I/O, GUI,数学,数据库评估;
-来自语言SDK;
-从第三方来源,例如从互联网下载。
-开发者也可以发布他们自己的库。
库的链接:
当编辑、构建和安装程序时,必须提供要搜索的库列表 -> 如果在源代码中引用了一个函数,但是开发人员没有显式地编写它,那么将搜索库列表来定位所需的函数 -> 找到函数后,将适当的目标文件复制到可执行程序中
将库集成到可执行程序中的两种不同方法:
1.静态链接——在静态链接中,库是单个目标文件的集合。在构建过程中,当链接器工具确定需要一个函数时,它从库中提取适当的目标文件并将其复制到可执行程序中。这种静态链接将库与开发人员自己的软件链接的行为发生在构建过程中。最后将一个可执行程序加载到目标机器上,在最终的可执行程序创建之后,不可能将程序与其库分离。
2.动态链接——动态链接方法不将目标文件复制到可执行图像中;相反,它指出成功执行程序需要哪些库。当程序开始运行时,库作为单独的实体加载到内存中,然后与主程序连接。动态库是通过连接目标文件构造的磁盘文件。然后将库收集到发布包中,并安装到目标机器上。只有这样,它才能被载入机器的内存。
动态链接的优点:
a.可以升级到库的新版本(添加特性或修复bug),而不需要重新创建可执行程序。
b.许多操作系统可以优化内存使用,只加载一个库的副本到内存中,但与其他需要相同库的程序共享它。
UML中的组件图
E. 组件-周期
版本控制系统(VCS)
软件版本控制是为计算机软件的唯一状态分配唯一版本名或唯一版本号的过程。
软件进化是软件维护中的一个术语,指的是最初开发软件,然后由于各种原因不断更新的过程。
典型系统的90%以上的成本发生在维护阶段,任何成功的软件都将不可避免地得到维护。
(3)运行视角
A. 概述
运行时:当程序在目标机中运行时,程序是什么样子的?目标机需要加载到内存中的所有磁盘文件是什么?
代码级视图:源代码——可执行程序的内存状态是什么样子的?程序单元(对象、函数等)如何相互交互?
组件级视图:架构——软件包如何部署到物理环境(OS、网络、硬件等)中,以及它们如何交互?
时刻视图:程序在特定时间内的行为
-时期观:他们是如何随着时间而表现的
本机机器码
程序首先加载到内存中,有几种执行软件的机制,这取决于程序加载前进行了多少编译,以及OS支持程序所需的操作系统。将可执行程序完全转换为CPU的本机机器码,CPU只是“跳转”到程序的起始位置,所有的执行完全使用CPU的硬件。在执行过程中,程序可以选择调用操作系统来访问文件和其他系统资源。这是执行代码最快的方法,因为程序完全访问CPU的功能。
完整的程序解释:运行时系统将整个源代码加载到内存中并对其进行解释(如BASIC、UNIX shell等)
解释字节码
字节码类似于本机机器码,只是CPU不能直接理解它们。
-它首先将它们翻译成本机机器码或在程序执行时解释它们。
因此,字节码环境要求在程序旁边加载一个额外的解释器或编译器。
Java虚拟机(JVM)
Perl或Python:它们被解释而不是编译,但是在运行时使用解释字节码。执行Perl或Python脚本的简单操作就会自动触发字节码的生成。
配置和数据文件
任何较大的程序都使用外部数据源,例如磁盘上的文件。程序调用操作系统来请求将数据读入内存。
-屏幕上显示的位图图形图像
-以数码形式储存的声音
-自定义程序行为的配置文件
-包含在线帮助文本的一组文档
-包含名称和地址的数据库
分布式程序
例如,软件系统可能使用客户端/服务器模型,在一台计算机上运行一个服务器程序,在许多其他计算机上运行大量客户机程序。在这个场景中,构建系统可以创建两个发布包,假定安装服务器程序的人与安装客户机程序的人不同。或者,可以使用相同的发布包安装这两个单独的程序。
B. 代码-时刻
快照图:关注目标计算机内存中的变量级别执行状态。
内存转储:硬盘上的文件,包含进程内存内容的副本,当进程因某种内部错误或信号而中止时产生。调试器可以加载转储文件并显示其中包含的有关运行程序状态的信息。信息包括寄存器的内容、调用堆栈和所有其他程序数据(计数器、变量、开关、标志等)。这是为了分析程序的状态,程序员查看内存缓冲区,看看哪些数据项正在工作时的失败。
C. 代码-周期
UML中的序列图:程序单元(对象)之间的交互
执行跟踪:跟踪涉及到专门使用日志记录程序执行的信息。这些信息通常由程序员用于调试目的,此外,根据跟踪日志中包含的信息的类型和细节,由经验丰富的系统管理员或技术支持人员以及软件监控工具用于诊断软件的常见问题。
D. 组件-时刻
UML中的部署图
E. 组件-周期
事件日志为系统管理员提供了对诊断和审计有用的信息。在开发周期中考虑将被记录的事件的不同类别,以及事件消息中出现的细节。
每一类事件都被分配一个唯一的“代码”来格式化和输出一条人类可读的消息。这有助于本地化,并允许系统管理员更容易地获取有关发生的问题的信息。
执行跟踪和事件日志记录
事件记录 软件追踪
主要由系统管理员使用 主要由开发人员使用
记录“低级”信息(例如抛出异常) 记录“高级”信息(例如,程序安装失败)
不能太“嘈杂”(即不能包含许多重复的事件或信息,对目标受众没有帮助) 可以吵闹
基于标准的输出格式通常是可取的,有时甚至是必需的 输出格式的限制很少
事件日志消息通常是本地化的, 很少关注本地化
添加新的事件类型以及新的事件消息不需要是敏捷的 添加新的跟踪消息必须是敏捷的
2软件构建:视图之间的转换