编辑
2026-05-09
记录知识
0

目录

前言
一、软件开发演进四阶段
阶段1:机器语言(1940 年之前)
阶段2:汇编语言(20 世纪 40 年代)
阶段3:高级语言(20 世纪 50 年代)
阶段4:两次软件危机与编程范式演进
二、软件架构为何在 1990 年代才出现?
演进规律:拆分粒度越来越粗
三、示例程序:编程范式演进对比
代码解读
总结
参考

前言

理解一个事物的本质,最好的方式是追寻它出现的历史背景和推动因素。本文梳理从机器语言到软件架构的演进脉络,揭示软件架构为何在 20 世纪 90 年代才真正流行。


一、软件开发演进四阶段

阶段1:机器语言(1940 年之前)

最早的软件直接用二进制 01 表示机器指令。例:在 8086 上做 s=768+12288-1280,机器码如下:

101100000000000000000011 ; 赋初值 000001010000000000110000 ; 加法操作 001011010000000000000101 ; 输出结果

三难:太难写、太难读、太难改。一个 1 敲成 0 就可能导致火箭坠毁(1963 年 Mariner 1 事故,源于一行 FORTRAN 代码错误)。

阶段2:汇编语言(20 世纪 40 年代)

用助记符替代二进制操作码,例如:

机器语言汇编语言
1000100111011000mov ax, bx

解决了"读"的问题,但依然面向机器——程序员必须精确了解 CPU 指令、寄存器、段地址等底层细节。同一个程序,为 Intel CPU 写一次,还需为 Motorola CPU 重新写一次,指令完全不同。

阶段3:高级语言(20 世纪 50 年代)

语言年份全称用途
Fortran1955FORmula TRANslator(公式翻译器)科学计算
LISP1958LISt Processor(枚举处理器)AI 研究
Cobol1959Common Business Oriented Language商业数据处理

高级语言让程序员不再关注机器底层,只关注具体业务。同一段代码,经编译器处理后可在多种 CPU 上运行。

lisp
; LISP 实现 4+6=? (+ 4 6) ; → 10

阶段4:两次软件危机与编程范式演进

第一次软件危机(1960s~1970s):软件规模复杂度激增,质量低、项目延期、超支严重。典型案例:IBM System/360 操作系统,2000+ 程序员、5000 人年、100 万行代码、5 亿美元投入,进度却一再延迟。

解决方案

  • "软件工程"概念诞生(NATO 会议 1968/1969)
  • Dijkstra 发表《GOTO 有害论》(1968)→ 结构化程序设计:抛弃 GOTO,自顶向下、逐步细化、模块化
  • Pascal 语言诞生(结构化语言的代表)

第二次软件危机(1980s):硬件快速发展导致业务需求和编程领域急剧扩展,核心问题从"逻辑复杂"变为"扩展复杂"。

解决方案面向对象编程(C++ → Java → C#),1967 年的 Simula 语言首次提出,但因第二次软件危机才真正流行。


二、软件架构为何在 1990 年代才出现?

20 世纪 60 年代 Dijkstra 就已涉及软件架构概念,但真正流行却是在 1990 年代。原因在于玛丽·肖(Mary Shaw)和戴维·加兰(David Garlan)在 1994 年发表的《软件架构介绍》中指出的:

"When systems are constructed from many components, the organization of the overall system—the software architecture—presents a new set of design problems."

翻译:随着软件系统规模增加,计算相关的算法和数据结构不再是主要设计问题;当系统由许多组件构成时,整个系统的组织方式(软件架构) 成为新的设计挑战。

换言之:只有规模足够大的系统,才需要软件架构。大公司(Rational、Microsoft)由于系统规模大,才率先遇到以下问题:

系统规模庞大 → 内部耦合严重,开发效率低 ↓ 系统耦合严重 → 牵一发动全身,修改扩展困难 ↓ 逻辑复杂 → 易出问题,出问题难排查修复

演进规律:拆分粒度越来越粗

模块(结构化编程,1960s) → 对象(面向对象,1980s) → 组件(软件架构,1990s) 粒度粗 粒度更粗 粒度最粗 层次低 层次更高 层次最高

本质:面对规模不断增长的系统,拆分粒度越来越粗,拆分层次越来越高,目的都是将复杂度控制在可管理范围内。

如果 IBM System/360 在 1990 年代开发,质量、效率、成本都会好得多——当然,我们就看不到《人月神话》了。


三、示例程序:编程范式演进对比

以下程序用三种方式实现同一个功能——计算并输出三个数的求和与平均值,演示从机器语言思维到高级语言思维的演进。

python
#!/usr/bin/env python3 """ paradigm_demo.py - 演示编程范式从机器思维到高级思维的演进 阶段1(机器思维):像汇编一样,直接操作寄存器/变量地址 阶段2(结构化思维):分步骤、自顶向下、模块化 阶段3(面向对象思维):抽象出数据和操作,消息传递 运行方式: python3 paradigm_demo.py """ from dataclasses import dataclass from typing import Callable # ───────────────────────────────────────────── # 阶段1:机器语言式的直接操作 # 不封装,像汇编一样直接处理数据地址 # ───────────────────────────────────────────── def machine_style_demo(): """ 模拟机器语言/汇编风格: - 直接操作数值,不封装 - 没有函数抽象,一步一步做 """ print("=== 阶段1: 机器语言风格(直接操作) ===") # 直接用数值计算,不命名,就像寄存器操作 a = 10 b = 20 c = 35 # 第一步:计算总和(像汇编一步一步来) total = a total = total + b total = total + c # 第二步:计算平均值 count = 3 average = total / count # 第三步:输出 print(f" 总和: {total}, 平均值: {average}") return total, average # ───────────────────────────────────────────── # 阶段2:结构化程序设计风格 # 自顶向下、逐步细化、模块化 # ───────────────────────────────────────────── def compute_sum(values: list[float]) -> float: """模块1:计算总和""" total = 0.0 for v in values: total += v return total def compute_average(total: float, count: int) -> float: """模块2:计算平均值""" return total / count def output_result(total: float, average: float) -> None: """模块3:输出结果""" print(f" 总和: {total}, 平均值: {average}") def structured_style_demo(): """ 结构化程序设计风格: - 函数封装(模块化) - 自顶向下分解 - 每个函数做单一职责的事 """ print("=== 阶段2: 结构化程序设计风格(模块化) ===") values = [10.0, 20.0, 35.0] # 自顶向下调用各模块 total = compute_sum(values) avg = compute_average(total, len(values)) output_result(total, avg) return total, avg # ───────────────────────────────────────────── # 阶段3:面向对象风格 # 抽象数据+行为,消息传递 # ───────────────────────────────────────────── @dataclass class StatisticsResult: """统计结果:聚合了多个相关数据""" total: float average: float count: int values: list[float] def report(self) -> str: return f"总和={self.total}, 均值={self.average:.2f}, 数量={self.count}" class DataProcessor: """ 数据处理器(对象): - 封装了数据和操作 - 对外暴露行为(方法),隐藏内部细节 """ def __init__(self): self._values: list[float] = [] self._total: float = 0.0 self._computed: bool = False def add(self, value: float) -> "DataProcessor": """链式调用:add() 返回 self""" self._values.append(value) self._total += value self._computed = False return self def compute(self) -> StatisticsResult: """计算统计结果(延迟计算)""" count = len(self._values) avg = self._total / count if count > 0 else 0.0 self._computed = True return StatisticsResult( total=self._total, average=avg, count=count, values=self._values.copy() ) def oop_style_demo(): """ 面向对象风格: - 数据和操作封装在一起 - 通过消息传递(方法调用)交互 - 可扩展、可替换(多态的潜在基础) """ print("=== 阶段3: 面向对象风格(封装+消息) ===") # 创建处理器对象,链式添加数据 processor = DataProcessor() processor.add(10.0).add(20.0).add(35.0) # 发送消息让对象执行计算 result = processor.compute() print(f" {result.report()}") return result.total, result.average # ───────────────────────────────────────────── # 主函数:运行并验证三种风格输出一致 # ───────────────────────────────────────────── def main(): print("编程范式演进对比演示\n") r1_t, r1_a = machine_style_demo() r2_t, r2_a = structured_style_demo() r3_t, r3_a = oop_style_demo() # 验证三种方式结果一致 print() assert r1_t == r2_t == r3_t, "计算结果不一致!" assert abs(r1_a - r2_a) < 1e-9 and abs(r2_a - r3_a) < 1e-9, "平均值不一致!" print(f"✓ 三种风格的计算结果一致:总和={r1_t}, 均值={r1_a:.4f}") print() print("演化规律总结:") print(" 机器语言 → 关注 '怎么做'(寄存器/地址)") print(" 结构化 → 关注 '分步骤做'(函数/模块分解)") print(" 面向对象 → 关注 '谁来负责'(对象/消息/封装)") if __name__ == "__main__": main()

运行效果:

bash
$ python3 paradigm_demo.py 编程范式演进对比演示 === 阶段1: 机器语言风格(直接操作) === 总和: 65.0, 平均值: 21.666666666666668 === 阶段2: 结构化程序设计风格(模块化) === 总和: 65.0, 平均值: 21.666666666666668 === 阶段3: 面向对象风格(封装+消息) === 总和: 65.0, 均值=21.67, 数量=3 ✓ 三种风格的计算结果一致:总和=65.0, 均值=21.6667 演化规律总结: 机器语言 → 关注 '怎么做'(寄存器/地址) 结构化 → 关注 '分步骤做'(函数/模块分解) 面向对象 → 关注 '谁来负责'(对象/消息/封装)

代码解读

阶段思维方式关键特征
机器风格直接操作,步步控制无封装,全是变量和临时计算
结构化风格分模块,单一职责函数分解,自顶向下调用
面向对象风格封装数据,消息传递类 + 方法,链式调用,延迟计算

三次演进的核心变化:抽象层次不断提高,程序员关注的问题域不断远离机器底层


总结

  • 机器语言→汇编→高级语言:解决"机器能看懂"的问题,核心是抽象机器细节
  • 结构化编程(1960s)解决第一次软件危机——逻辑复杂,方法是自顶向下模块化
  • 面向对象(1980s)解决第二次软件危机——扩展复杂,方法是封装+消息传递
  • 软件架构(1990s)是大规模系统催生的新学科,解决的不是新危机,而是系统组织问题
  • 演进规律:拆分粒度从模块→对象→组件,粒度越来越粗,层次越来越高,本质都是控制复杂度
  • 没有银弹:结构化、面向对象、软件工程、架构设计各自解决了特定历史背景下的问题,但业务永远在变,技术永远在演进

参考

  • 软件架构基础
  • Frederick P. Brooks,《人月神话》, 1975
  • Mary Shaw & David Garlan,《An Introduction to Software Architecture》, 1994