校园预约页面已经收到请求。这次管理员把一个时段的名额改成三个,并要求同一个人不能重复占位。后端要执行判断,开发团队要讨论哪些对象和服务负责它,验证人员还要说明“不会超额”依赖什么条件。一条规则会出现在源码、设计模型和数学规约里;三种表达管理的对象不同,可以在修改过程中交替使用。
本节沿教材的两部分阅读:先拆开语言的组成,再看语言的类别。分类中既有面向处理器的表示,也有面向系统设计和数学约束的语言。它们的抽象层次、目的与执行方式需要分别判断。网络请求怎样到达服务见2.5的传输与业务证据;这里从服务准备处理规则的地方继续。
2.6.1 计算机语言的组成:把规则写成值、路径与容器
暂时忽略并发,把已占位用户放在集合 S 中,上限叫 cap。一小段教学伪代码就能看出几个职责:
count = size(S)
if count < cap and user not in S:
S = S union {user}
else:
reject
count、cap和S是变量;直接写下的 3 是字面量。若把一个确定值绑定给常量名 MAX_CAP,这个名字与字面量又不是同一件事。关系运算 count < cap 产生一个真或假的值,布尔运算把几个条件合起来。表达式首先回答“求得什么值”;它可以作为控制语句的条件,也可以用于赋值、参数或别的表达式。
if 根据条件选择执行路径。循环让某段动作重复,函数组织调用与返回,异常处理让失败进入另一条路径。教材把这些放在流程控制中。控制语句也会使用表达式,不应把它理解成与数据求值完全分离的机器。
S 用于保存用户。教材的“集合”是较宽的数据组织类别,列出字符串、数组、散列表等。数组便于按位置找元素,散列表按键组织查找;数学集合还强调成员是否属于其中,不保留重复成员。选了数据结构以后,仍须另外定义业务规则。例如,列表容许两个相同用户出现,并不意味着预约系统允许重复占位。

这个顺序写出的逻辑在单线程示意中清楚,但多个请求同时“检查后再加入”仍可能超额。语言能表达规则;实现还要把检查与更新放在合适的事务或同步边界。2.3的同步与互斥讨论的是执行时怎样保护这段变化。
2.6.2 计算机语言的分类:先看表达对象,再看处理方式
早期的机器、汇编和高级语言常按接近硬件的程度排列。机器语言是指定指令集架构(Instruction Set Architecture,ISA)的编码,处理器按该架构识别指令。汇编语言用助记符、寄存器名、符号地址表达相关操作,由汇编器翻译;它仍依赖指令集及汇编方言。高级语言把很多硬件细节交给语言实现,让人用函数、对象、控制语句等描述任务。
接近硬件并不自动保证更快、更省空间。手写指令、编译器优化、算法与访问模式都会影响结果。高级语言的可移植性通常指源级可移植:语言实现、标准库、操作系统接口、数据宽度与外部依赖符合条件时,源码更容易在另一平台构建或运行。一个已编译的原生二进制通常还受 ISA、ABI 和运行环境约束;不能由“同一语言”推出“文件在哪台机器都能执行”。
C、C++、Java 和 Python 都属于高级语言。C 提供较接近存储与硬件操作的表达;C++在此之外支持类、泛型等抽象;Java通常围绕虚拟机与类库运行;Python支持交互使用及多种程序组织方式。用途、程序范式与实现策略可以同时描述一种语言。教材提到的 C18、C++20等反映其成书时期;C23对应 ISO/IEC 9899:2024。选标准时须注明版本,不把教材中的“最新”或标准已经发布当成工具链已经完整支持的保证。
建模语言与形式化语言加入这份分类后,问题换成了“需要描述系统的哪一面”。UML用于表达模型元素与关系;Z等形式规约把状态和允许变化写成数学约束。它们可以与程序设计语言合作,不能仅凭“更高级”三个字把它们放在处理器逐层翻译的顶端。
地址字段少了,信息没有消失
预约服务最终执行的指令,需要规定操作以及输入、结果的位置。教材称为操作码和地址码;这里的“地址码”可以直接给操作数,也可以指定寄存器或存储位置。它不是每格都必须指向主存的地址表。
在教材的抽象三地址格式中,A1、A2给两个源,A3给结果;二地址格式里,A2兼作第二个源和结果。单地址格式省去另一个源和结果的位置,约定使用固定寄存器。栈式零地址算术指令把输入位置隐含为栈顶,操作后把结果压回。少写的是显式字段,输入和写入责任仍需完整确定。真实ISA的操作数顺序另查其规范,不能默默把教材的二地址结果改成 A1。

下一条指令也需要位置。程序计数器(Program Counter,PC)通常保存取指相关地址;顺序执行按指令长度更新,控制转移则改到目标位置。教材的 PC+1 明确假设一条指令只占一个主存单元。换成字节编址、四字节指令时,顺序步进的含义就改变,不能把“加一”无条件照抄。可变地址数指令的字段数还能随操作变化,这同样属于所选机器的格式约定。
汇编器处理什么,CPU执行什么
汇编源程序中的语句分成三类,区别来自处理者、时刻和产物。实际指令语句如特定方言中的 ADD,会编码成机器指令,运行时由CPU执行。工具指示通常叫伪指令或汇编指示;它让汇编器定义数据、符号、段或空间。数据定义可以输出初始化字节,但没有对应CPU操作的操作码。
因此,“伪指令不产生机器代码”要在指令编码的意义下理解。GNU as 的 .byte 会生成数据字节;符号赋值与空间保留又各有作用。“没有CPU操作码”不能推成“整个目标文件没有新增字节”,保留未初始化空间也不等于文件中已经写入同样多的零。
宏调用在汇编处理时展开为源语句,展开内容再交给汇编器。宏体既可含实际指令,也可含数据定义。运行时函数调用按语言语义转移控制并返回;优化实现还可能内联。两者首先是展开与执行发生的阶段不同,缺少宏体、运行路径与优化条件时,不能比较指令数、速度或开销。

教材把一条汇编语句拆成名字、操作符、操作数、注释四个字段。标号帮助引用位置,操作符规定任务,操作数字段提供参数,注释帮助人理解。它们是源码语法字段,不是四个机器地址字段。教材的 DB/DW/DD、SEGMENT/PROC与冒号规则有汇编方言背景,不能套到所有汇编器。
从源码到可执行产物:停在哪,留下什么
“C程序经编译得到什么文件”在旧题里常指常规原生目标代码,题库标答为机器码。但若没给工具、停止选项和目标形式,答案的适用范围就不够明确。以GCC常见本机目标C工具链为例,四个处理职责是预处理、编译、汇编、链接。这个补充解释处理方式,仍属于2.6.2,不增加教材正式小小节。
预处理展开头文件与预处理宏等。编译过程可以包括词法、语法、语义分析,中间表示(Intermediate Representation,IR)、优化及目标代码生成。汇编把汇编表示转换成目标代码;链接合并需要的目标文件与库,处理它们的符号和引用。目标文件还可含数据与元信息,不能把整个文件当纯机器指令串。

同一份可成功处理的C源码,gcc -E 在预处理后停止,通常把文本写到标准输出;gcc -S在编译后停止,通常留下 .s 汇编文本;gcc -c完成编译、汇编但不链接,通常产生可重定位 .o。完整构建才继续得到相应可执行产物或库。-S并非只执行前端分析,已经包含相应的目标生成;.o也不等于已链接的独立可执行文件。
编译器可能融合某些职责,集成汇编器也可能直接输出目标文件,不先写独立 .s。链接时优化(Link-Time Optimization,LTO)还可让目标文件包含内部表示。因此上面的图是指定实现的过程示意;“所有编译器都输出机器码”“每次都留下四种文件”都超出了它的条件。文件准备好之后,还要经过加载与运行环境;无需原编译器参与,不代表无需动态库或运行时。
先编译,再解释,也可以在运行时编译
Java 的普通 javac 调用把源码生成 .class,其中的字节码交给Java虚拟机(Java Virtual Machine,JVM)。JVM实现可以解释执行,也可以使用即时编译(Just-In-Time compilation,JIT)生成本机代码;不要求每个实现采用同一策略。编译到字节码与解释字节码可以先后出现,这足以说明“编译”和“解释”并非永久贴在语言名称上的两种互斥标签。
以CPython的常规字节码解释路径为例,源码先编译成代码对象,包含其执行使用的字节码,再由解释器执行。导入模块时可以有 .pyc 编译缓存,是否保存取决于运行入口、设置等条件。.pyc不是无需Python运行环境的原生可执行文件;有缓存也不意味着每次运行必须持有原始源码文本。执行策略依版本和构建配置;Python 3.14官方文档还描述可选的实验性JIT,不能把这幅示意推广为所有CPython都无JIT。

于是,判断时先问:正在讨论哪种语言实现、哪一步处理、哪种目标表示。再问它如何执行,以及移植所需的环境。只见“解释型”便断言无任何编译产物、只见“高级语言”便断言完全不受平台约束,都漏掉了条件。
UML:模型元素、图和视图各分各的
管理员担心限额修改会影响取消预约与统计。团队需要把规则放回系统整体:谁参与,谁负责,彼此如何联系,发生什么变化。Booch、OMT、OOSE等方法的统一工作形成了统一建模语言(Unified Modeling Language,UML);它给模型提供元素、关系、图和共同约定,本身没有规定团队必须采用哪套开发方法、在哪一天交哪份工作产品。
这里最容易混在一起的是三条分类轴:事物指教材组织模型元素的方式;图指用哪些表示来呈现模型;架构视图指针对哪类关注者和问题选择模型信息。知道一个词在其中一条轴上的位置,不会自动得到它在另外两条轴上的答案。

教材把事物分成四组。结构事物包括类、接口、协作、用例、主动类、构件、制品、结点,共八项。行为事物包括交互、状态机、活动,共三项。包是分组机制,注解用于解释与约束。旧笔记所列七结构、两主要行为与本版教材的范围不同,不能让制品、活动在新复习中消失;八和三也只是这份教学列表的数量,不是OMG规范全部元类的计数。
主动类(Active Class)仍是类,实例拥有自身行为控制;活动(Activity)是一种行为。中文相近,分类对象不同。协作规定协作中的角色和结构,不因名字像动词就改成行为事物。制品可为源码、脚本、可执行文件等,是模型中的可部署或可使用的信息制品;构件表达封装与接口,二者不能直接等同。结点也可表示软件执行环境,不能把所有结点都画成一台硬件机器。
包组织模型元素和命名空间,能容纳不同类别的元素。构件可从开发时的设计逐步细化到部署与运行;“包只在开发时、构件只在运行时”的排他说法不足以概括规范语义。共同机制还包括规范说明、修饰、公共划分与扩展机制;细节通常放在元素属性、约束、构造型等位置,不靠一张外形图完整表达。
图的数量必须带版本。OMG正式索引把UML 2.0采用日期列为2005年7月,教材源图中的2001年不能沿用为正式版本日期。其2.0规范列13种图;本次另核2.5.1分类,列14种,多了Profile图。下面用2.5.1的叶图分类帮助定位,交互图是家族,不再作为第十五种加一次。
| 图的主要类别 | 叶图与本节要看的对象 |
|---|---|
| 结构图,7种 | 类图:类及关系;对象图:某次实例配置;构件图:封装与接口;组合结构图:分类器内部部件、端口和连接;部署图:制品与结点;包图:模型组织与依赖;Profile图:针对领域的构造型等扩展。 |
| 行为图中的3种 | 用例图:外部角色与目标;状态机图:状态与转换;活动图:控制和数据流。 |
| 行为图中的交互家族,4种 | 序列图:消息先后;通信图:连接组织与编号消息;交互概览图:多个交互及其控制流;计时图(Timing Diagram):显式时间上的状态或值变化。 |
4+1:让同一系统回答五组关注
把预约服务交给前端、开发、运行与测试人员,他们需要的信息不同。Kruchten的4+1模型用逻辑、进程、开发、物理四个主要视图,加重要场景来联系和验证架构。教材列用例、逻辑、进程、实现、部署五视图;其中开发/实现、物理/部署的关注可对应,用例需求与场景验证则联系其它关注。它是一种架构表达方法,与UML语言的图分类应分开。
逻辑视图看功能服务与关键抽象:预约、时段、用户有哪些职责和关系?类图常用,类内部行为也可由状态机补充。进程视图看运行时并发、同步、通信及相关性能:哪些请求可同时处理,什么资源需要协调?开发视图看模块、包、子系统与代码依赖:团队怎样分工,某个接口改变会影响哪里?物理视图看软件怎样映射到部署结点,考虑不同配置下的可靠性、规模与性能。

“+1”场景可以是一次成功预约,也可以是容量已满或结点失效。它帮助发现架构所需内容,再验证多个视图能否共同解释这次行为。它不限于一张用例图,具体消息脚本、对象配置也能说明场景。
图与视图没有一对一专属配对。一张序列图可以解释逻辑协作,也可观察运行通信;部署问题也需要开发模块的信息。题目若指定“需求分析中的领域结构”,包图与类图是合理线索;若指定软件部署映射,则应关注部署图。不能看到“总体架构”就无条件选择同一对图,也不能将原论文列出的主要读者写成“其他岗位一律不看”。
用例的箭头表达关系,执行顺序另作说明
先画当前系统的边界。学生是系统外的预约角色;学生是否也在内部用户数据中出现,是另一条建模问题。参与者(Actor)表示与系统交互的角色,不限于真人;外部设备或系统也可能担任角色。把“时间”作为参与者时,必须明确边界外有怎样的触发源,不能让所有名词自动成为参与者。
在本例中,提交预约每次都包含校验预约信息,用 «include» 从提交指向校验。这是行为复用依赖;箭头自身没有给出消息时间、事务边界或所有错误路径。登录会话有效可以是提交的前置条件,不代表每次提交都要重新走完整登录用例。
另定义基础用例查看预约结果:即使不打印,屏幕结果也完整有意义。它在显示完成处提供一个扩展点;当用户要求纸质凭证时,打印凭证作为扩展用例插入,用 «extend» 从打印指向查看。这里明确给了扩展点与条件;“打印发生较晚”本身不足以证明扩展。规范也允许扩展条件缺省,不能把“必须写一个显式守卫”当作语法要求。
再定义预约场地为一般用例,预约研讨室为满足其契约的特殊用例。泛化用实线空心三角,从特殊指向一般。题干说“UC1可用于UC2出现的位置”,要判断的是可替代的一般/特殊关系;抽取UC2中的一段公共步骤并不使UC1能替代整个UC2。

参与者之间的特殊化也可用泛化,例如研究生作为学生的一种角色;参与者与用例之间常用关联表示交互。题目让你在用例的包含、扩展、泛化与类的聚合间辨析时,先确认两端是什么模型元素,再看题干的否定词。聚合的整体—部分含义不能替代这里的行为复用。UML允许不同元素在同一视图中出现,判断须看关系端点与语义,不能简化成某张纸上绝对不能出现任何菱形。
旧教学案例仅凭“考试后批阅”就写扩展,条件不足;也不能由“选择专业前已登录”推出必含完整登录行为。新例把必要的前置条件、扩展点和可替代约束重新写全,练习标为改编或自编,不冒充原题图的还原。
类的关系:先确认类型、使用还是整体—部分
讨论实现时,同样的箭头形状可能落在另一组元素上。假设研讨室是一种场地,泛化的空心三角仍指向一般类型。预约服务实现预约接口,用虚线空心三角指向接口契约;实现关系还可用于其它实现与规格,不限于这一个类→接口例。
若预约页面依赖预约接口,接口变化可能影响页面,依赖用虚线开放箭头从客户指向供应者。它不自动表示页面要在接口之前或之后运行;临时参数只是可能出现依赖的一种情境,不是定义依赖的唯一条件。
预约记录与场地长期有关联时,用实线表达连接,并把多重性写在相关端点:场地端 1、预约端 0..*,表示固定一条预约,关联一个场地;固定一个场地,可关联零至多条预约。数字读对端;它不是现有数据库的行数,也不由导航箭头决定。不显示多重性时,不能从省略的图形擅自推出数量为一。

共享聚合以整体端空心菱形表达整体—部分。精确的共享、所有权和生命周期语义需要领域模型补充,不能从空菱形单独推出一套统一删除规则。组合则用实心菱形:对象部件同一时刻至多属于一个组合整体,删除整体会删除仍包含在其中的对象部件;若先将部件移出,再删除整体,部件可以保留。
可以把预约记录与只属于该记录、删除时一起删除的内部明细建模为组合,并把这些约束写进模型。它没有要求明细与记录在同一时刻创建。泛化的“一种”与组合的“组成”描述不同关系;接口契约、导航性、数量约束又各有各的位置,不能由一个名称把它们互相代换。
定义、快照和消息:换掉观察对象,图也跟着换
类图定义预约记录有哪些属性、操作和关系;对象图展示某个时刻 b17:预约 处于已确认、关联哪一个具体场地。这是类与实例配置的区别。对象图可以帮助检查某条结构约束,不能仅凭图名里的“对象”就用它说明对象整个生命周期的变化。
如果要追踪提交过程,序列图把参与交互的生命线排在上方,消息按发生先后安排。生命线代表一个参与者,不要求永远是一份可见的具体对象源码;竖直虚线也不是活动分区的泳道。同步调用表示调用方等待被调用行为完成,异步调用允许调用方继续,与实际耗时是否很短是两回事。

序列图主要给消息先后与相关约束,图面纵向距离通常不是秒数;显式时间观测、持续时间约束仍可加进模型。计时图则用明确时间轴观察状态或值随物理时间的变化。题目要“某时刻的变化规律”时,应读是否给出了实际时间尺度,不能靠“时序”“计时”的译名猜。
通信图更突出参与者连接,编号表达消息顺序。它与简单序列图可以描述相应交互,但规范并未保证所有复杂交互片段、交互引用都能完整等价转换。交互概览图把较大交互及其引用放进控制流概览;外形接近活动图,也不意味着节点都变成普通业务动作。四种交互图不包含状态机图或活动图。
一条预约的状态,与处理它的工作流
同一次预约提交,可以从两个问题观察。第一问:“这条预约现在处于什么状态,接到事件后允许怎样变?”状态机把草稿、已确认、已取消这些状态和相应转换写出来。第二问:“校验、判断、保存、通知如何流转?”活动图追踪控制和数据,动作可以由不同职责执行,也可描述同一个算法。

状态转换常以“触发事件 [守卫] / 效果”理解,守卫为真才允许相应转换。状态机还可有完成事件、内部转换、复合状态与并行区域;不能将入门示意扩大成“只能单个具体对象、只响应外部事件”。入/出状态的行为描述模型规则,也不能自动保证机器指令级原子性。
活动既能表示控制流,也能表示对象数据流和等待外部事件。活动分区常被叫泳道,用于表示责任划分;没有画泳道仍可以是活动。分叉可以引出并发流,默认汇合等待各入边都有相应令牌,合并则收拢替代路径,二者不同;给定别的 joinSpec 时还要按模型条件解释。画出两条分支,未给资源与时长,不能凭图推出两台CPU或完成时间。
需求分析中的实体、边界和控制类,也是在分职责:实体承载业务信息,边界承载系统内外的交互,控制协调用例过程。分析类着重领域概念,设计类增加软件接口与协作细节;迭代开发可以不断细化,不必把每种类的产生排他锁定到一个固定阶段。它们不与UML的结构/行为事物直接一一对应。
把“不超额”写成可检查的契约
图片让团队看清关系,但“加入一个人以后仍不超上限”还需要更精确的条件。形式化方法把规格、设计或验证对象放进具有明确语法与语义的数学模型,并针对给定规范证明性质。Z以集合论与一阶谓词逻辑为基础,使用框架(Schema)组织声明与谓词;它能描述状态不变量、操作输入输出和前后状态关系。
只看一个时段,以不同用户集合 S、非负整数上限 cap 表示状态。本例约定自然数包含零,|S| 是不同成员数,USER 是允许的用户集合。下面是自编Z风格集合契约示意,用于解释条件,不声称它是一份已通过标准解析器的完整规约。
状态:S ⊆ USER,cap ∈ {0,1,2,…},|S| ≤ cap
Add(u?) 的成功前提:u? ∈ USER,u? ∉ S,|S| < cap
成功后置:S′ = S ∪ {u?},cap′ = cap
u?是输入,撇号标后状态。设原来 S={甲,乙}、cap=3,输入丙,三人都属于USER。丙是新成员,且 2<3;成功后 S′={甲,乙,丙},不同成员数为3,仍满足 3≤3。这里人数与容量都是离散计数,没有换算成字节或时间。

改一个条件:保持甲乙与输入丙,把容量变成2。原状态仍合法,因为 2≤2;添加前提 2<2为假,成功Add不能执行。这说明状态合法与操作可执行是两次不同判断。若错误实现漏了容量条件,直接并入丙,得到3人却只有2个名额,3>2违反不变量。这是错误操作的反例,不能标成合法Add的后状态。
再改输入为已经在S中的甲,并集本身不会增加成员数,但当前契约要求 u?∉S,所以该成功操作仍不适用。若要把重复提交做成幂等成功,需另定义允许重复的操作与后置;结果看起来没变,不会替你修改契约。容量为零时合法集合只能为空,0<0不成立,也不允许添加。
一般证明同样清楚:输入在USER中,故并入后仍为USER的子集;新成员不在S中,故 |S′|=|S|+1;基数与容量为整数,由 |S|<cap 推出 |S|+1≤cap=cap′。逐个列成员核算与这条一般推理结果一致。它证明的是给定成功契约保持这个容量不变量;失败后的错误码、真实并发实现、数据库事务、多时段分配都还未在模型中规定。
这份集合契约突出状态和操作,尚未规定时间与并发交互。教材还从几条轴介绍形式化方法:数学基础、说明途径、应用关注、描述方式与表达能力。先看每一轴在问什么,再读类别。同一方法可以具有多个特征,不能把表内名称一起塞进互斥树。
| 教材分类轴 | 在比较什么 | 类别及边界 |
|---|---|---|
| 数学基础与说明途径 | 用哪些数学对象表达规格 | 公理方法用前后条件;集合论与一阶谓词途径可用Z、meta-IV;代数规格描述抽象数据类型及操作规律;进程描述表达计算与通信行为,如CSP、CCS。 |
| 应用/规约风格 | 模型重点与关注问题 | 教材列“面向对象”、面向属性、基于并发、基于实时四类。其第一组借状态/操作举Z、VDM等例,普通Z的这种风格不自动等于面向对象的封装/继承;Object-Z等扩展另辨。属性、并发与实时关注也可交叉。 |
| 描述方式 | 怎样规定系统 | 模型描述直接构造数学模型;性质描述通过应满足的性质间接约束系统。一个模型仍可以被证明满足性质。 |
| 表达能力 | 怎样组织状态、操作与行为 | 教材列模型、代数、进程代数、逻辑、网络模型五类。模型显式给状态/动作;代数强调操作关系;进程代数表达并发交互;逻辑表达系统性质;网络模型如Petri网显式建模并发结构。这里的网络模型不等于2.5的通信设备拓扑。 |
CSP、CCS是进程代数,process描述计算/通信行为,不能误读成软件开发进程。并发模型也可借逻辑表达性质;模型与性质逻辑彼此配合,不能因此把两层名称合成一种“逻辑方法”。教材中某些语言归类、旧缩写及“不能显式表示并发”的概括应保留其基本模型口径,不扩大成所有状态模型、所有代数都不能表达并发。通信协议中使用的LOTOS、ESTELLE、SDL等属于教材所举历史规格语言线索;本节不将书中的“目前认可”改称已审遍今日所有规范版本。
教材把形式化开发放进六个环节:可行性分析先综合评价,现实约束与自然语言难以全交给符号演算;需求分析把需求明确成规格;体系结构设计描述接口、功能与结构,可结合半形式化表示;详细设计在架构规格基础上逐步精化;编码实现精化结果,受支持语言和工具条件限制时可自动生成部分代码;测试发布检查文档与实现,并可依据模型生成测试用例。
六环节是一种开发组织,不是六种数学语言,也不是每次使用Z都要重复的固定工具链。模型生成测试要明确覆盖准则与模型边界,不能直接保证实现的所有路径和需求已经覆盖。采用数学语言不会自动补全需求,不会自动让错误输入有定义,也不会自动证明实际代码与模型一致。正确性要写明针对哪份规范、哪些假设以及怎样的精化与实现证据。
回到名额调整:表达式给出值,控制选择路径,数据结构承载状态;机器与汇编说明硬件操作怎样被表达,工具链与运行实现决定它怎样执行。UML把系统的不同关注放进相应模型,形式规约进一步写清允许的状态和变化。遇到相近名称,先确认它描述的对象与给定条件,再选择图、产物或关系;具体性能怎样评估,将在2.9系统性能继续。