案例题里「架构风格」考的是三件事:
- 系统长什么样(构件)
- 谁和谁怎么连(连接件)
- 数据或控制怎么流(约束)
当我们设计任何一个软件系统时,核心要解决的问题无非是"构件之间如何交互"以及"数据与控制权如何流转"。这五大风格恰好穷举了系统运转的几种根本动力。
- 如果一个系统的核心诉求是让数据像流水线一样经过层层加工和转换,那么它自然归属于以数据流转为驱动的 数据流风格 。
- 如果系统需要明确的上下级控制关系,上层指挥下层干活并等待结果,这就构成了以同步控制为主的 调用/返回风格 。
- 随着系统规模变大,当我们希望各个模块解耦 ,彼此不知道对方的存在,仅通过丢出事件或消息来协作时,就演化出了 独立构件风格 。
- 而面对那些业务规则极其多变 、甚至要求每天都在变的场景 ,把逻辑写死在代码里显然行不通,这时候就需要引入 虚拟机风格 ,在系统内部造一个能动态解释脚本或规则的引擎。
- 最后,如果多个独立的业务模块不需要互相调用,而是全部围着一个核心的数据源(比如数据库或黑板)进行读写协作,这种以数据共享为绝对中心的模式就是 仓库风格 。
理解了这种划分的内在逻辑,在做案例题或进行真实的工程选型时就能做到直觉式的判断。你不需要死记硬背,只需要观察题干中系统的"运转重心"在哪里。
- 重心在流转的数据上就是数据流,
- 重心在方法调用上就是调用返回,
- 重心在消息队列上就是独立构件,
- 重心在动态规则上就是虚拟机,
- 重心在共享数据库上就是仓库。
这种从设计哲学出发的认知方式,不仅能帮你准确命中考试的得分点,也能让你在实际工程中更敏锐地洞察系统本质。
一、基本概念
1. 什么是架构风格
架构风格描述的是一类系统的结构组织方式 ------不是具体怎么做,而是这一类系统共同的长法。它约定:
- 一组构件类型(如过滤器、层、服务)
- 构件之间的连接方式(如管道、调用、事件)
- 构件如何组合为系统
- 对构件交互的约束
一句话公式: 架构风格 ≈ 构件 + 连接件 + 约束。
双轨对照(三个词在卷面 vs 在工程里):
| 教材词 | 考试(卷面可写) | 工程落地(脑子里对应什么) |
|---|---|---|
| 构件 | 过滤器、层、进程、知识源等结构块 | 微服务实例、网关、领域服务包、前端页面模块 |
| 连接件 | 管道、调用、消息、事件、读写库 | HTTP/gRPC、MQ、领域事件总线、JDBC、共享表 |
| 约束 | 单向依赖、只能调下层、发布订阅规则 | 分层依赖注入方向、DDD 边界、Kafka 消费组语义 |
2. 架构风格 vs 架构模式 vs 设计模式
| 概念 | 粒度 | 考试关注点 | 考试举例 | 实际开发对应 |
|---|---|---|---|---|
| 架构风格 | 系统级 | 整体家族特征、谁连谁 | 分层、管道-过滤器、C/S | 一整个后端是「三层」、整条链是「网关→服务→库」 |
| 架构模式 | 系统/子系统级 | 解决大块重复问题的一套路 | MVC、微内核、Broker | 一个前端工程用 Vue+MVVM 、IDE 插件体系、消息中间件做中介 |
| 设计模式 | 类/对象级 | 代码级复用与解耦 | 单例、工厂、观察者、策略 | Spring Bean、Listener 、Strategy 接口替换实现 |
类比(只帮助记忆,不必写卷子上):
- 架构风格 :整栋楼是「板楼 / 塔楼 / 别墅」这种大类长相。
- 架构模式 :某一单元里「客厅怎么兼书房」这种常见套路(仍偏大块)。
- 设计模式 :家具怎么摆、插座留几个------代码/类级别的套路。
考试中「架构风格」与「架构模式」经常混着说 ,案例题只要你能说清结构 + 连接 + 适用场景,不必在卷面上抠字眼。
二、五大架构风格分类
教材将架构风格分为五大类,每类下有具体的子风格。下表一行一类 ,第四列每类只记一个代表例子(够写案例题即可);更多「工程锚点」见第三节各小节的双轨对照。
五大类总览(整合版)
| 大类 | 子风格 | 考试常抓的关键词 | 代表例子(每类记 1 个) |
|---|---|---|---|
| 数据流风格 | 批处理序列、管道-过滤器 | 数据沿拓扑流动;批处理「整批再交下一步」,管道「边流边处理」 | 离线数仓 ETL:按固定顺序「抽取 → 清洗 → 转换 → 加载/出报表」,前一步整批跑完再交下一步(批处理序列) |
| 调用/返回风格 | 主程序/子程序、面向对象、分层 | 显式调用链、层次依赖、上层用下层服务 | Web 三层:Controller → Service → DAO(或 Repository)→ 数据库 |
| 独立构件风格 | 进程通信、事件驱动(隐式调用) | 进程/服务独立,靠消息或事件对齐,发布方不知谁消费 | 支付成功发 MQ:库存扣减、积分、通知各起消费者异步处理 |
| 虚拟机风格 | 解释器、基于规则的系统 | 解释执行或规则匹配,行为随配置/脚本变 | 嵌入式 Lua :游戏/网关在宿主进程内嵌 Lua 解释器,改 .lua 即改逻辑,主程序只提供 API(典型解释器风格) |
| 仓库风格 | 数据库系统、黑板系统、超文本系统 | 共享中枢;多构件读写给「板」;或节点超链跳转 | 多系统共库:各子系统读写同一套业务表协同(数据库系统) |
三、每种架构风格详解
1. 数据流风格(典型例子:离线数仓 ETL、日志清洗链)
1.1 批处理序列(典型例子:银行日终对账)
定义: 数据按照一系列固定顺序的处理步骤依次传递,每个步骤处理完全部数据后,再将结果交给下一步。
结构: 步骤1 -> 步骤2 -> 步骤3 -> ... -> 最终结果
直觉与适用: 同一套逻辑串起来记------银行日终 (汇总当日流水 → 计息 → 入账 → 出报表)、大促后离线对账单 等,都是上一步整批跑完才把结果交给下一步 ,中间常靠文件或结果表 交接;教材里说的传统 ETL、数据报表、离线数据处理(数仓日批、Hive 分区跑数、报表任务)走的都是这种「批处理序列」。
特点:
- 每个步骤是独立的程序
- 步骤之间通过文件或数据集传递数据
- 前一步必须全部完成后,下一步才能开始
优点:
- 运维心智简单 :每步输入输出清晰(如「昨日分区 → 清洗结果表 → 汇总表」),排错时按阶段查日志即可。
- 失败隔离 :某一阶段失败可单独重跑,不必从头重算全流程(配合分区/幂等设计)。
- 资源可规划 :大作业可安排在业务低峰窗口 ,与在线系统错峰抢资源。
缺点:
- 时效差 :结果天然滞后(T+1、小时级),不适合「下单秒级反馈」类交互。
- 中间落地成本 :步骤间常写大量中间表/文件 ,占存储且要有清理与对账策略。
- 交互弱 :用户不能中途改规则重跑单条,除非另做补数/回刷流程。
1.2 管道-过滤器(Pipes and Filters,典型例子:API 网关过滤器链)
定义: 每个处理步骤称为"过滤器",数据通过"管道"在过滤器之间流动。与批处理不同,过滤器可以增量式处理数据(不必等前一步全部完成)。
结构: 数据源 -> |过滤器A| -> |过滤器B| -> |过滤器C| -> 数据汇
直觉与适用:
- Linux 管道 :日志经
cat→grep ERROR→awk打印某列------上一段有输出,下一段就能接着处理,不必等整个日志读完。 - API 网关过滤器链 :请求依次经过认证、限流、路由、访问日志等插件,每个插件只处理「流过的 HTTP 请求/响应」,不必等全站流量结束(与管道-过滤器一致)。
教材中的 Unix 管道、网关插件链、日志清洗入 ES、Flink/Kafka Streams 算子链、编译器词法→语法→代码 等,都归在管道-过滤器下记。
优点:
- 职责边界清晰:每个过滤器只做一件事(例如只做脱敏、只做 JSON 解析、只做限流),修改某类能力时尽量只动该段,不必在「上帝类」里翻逻辑。
- 复用与拼装 :同一过滤器可接到不同管道(如「脱敏」既用于离线导出,也用于实时日志);新需求常见是增加一段 或替换一段,而不是重写整条链路。
- 吞吐与并行 :流式场景下多段可流水线重叠 (上游产出下一批时下游已处理上一批);数据可分片时还可多路管道并行,提高单位时间处理量。
- 扩展成本低 :网关加鉴权插件、Flink 加 map/filter 算子,只要管道上的数据契约(字段、格式)稳定,上下游可独立演进。
缺点:
- 不适合强事务、强编排 :例如「下单」要同时 扣库存、扣款、写订单,三步要么全成要么全败 ;若硬拆成三段管道、各处理一条流过的消息,管道本身很难像数据库单库事务那样一步原子提交 ;往往要在管外再做 Saga 或流程编排 ,或每段反复查库校验,简单流水线的优势就被抵消。
- 格式转换与适配负担 :上一段输出 CSV、下一段要结构化对象,中间要适配层 ;任一环节改字段名/类型,可能牵动多个过滤器的解析逻辑。
- 排错与补偿难 :中间一环失败时,要定位是哪条记录、哪一段 出问题,日志链路往往比「一次 RPC 进业务层」更长;Kafka 等场景下还要处理部分成功、offset 已提交与重试的边界。
- 难做跨过滤器强事务 :管道上通常没有全局两阶段提交,一致性靠幂等、重试、最终一致,设计和运维成本更高。
Saga 是啥(记一句): 把一件大事拆成多步 ,每步在各自库里本地提交 ;中间某步失败时,按约定做补偿 (例如已扣款则退款、已减库存则加回),用最终一致代替「跨服务两阶段锁」,常见于微服务下单、出行多段预订等。
考试高频对比:管道-过滤器 vs 分层
| 维度 | 管道-过滤器 | 分层 | 工程差异(一眼区分) |
|---|---|---|---|
| 数据流向 | 线性数据流 | 层间调用/返回 | 管道:同构数据 在链上变形;分层:请求自上而下再返回 |
| 交互方式 | 数据驱动 | 接口调用 | 管道:拉/推流 ;分层:同步 RPC/方法 为主 |
| 构件关系 | 各过滤器独立 | 上层依赖下层 | 管道:少共享状态 ;分层:领域层可复用、依赖方向清晰 |
| 适用场景 | 数据处理流水线 | 企业应用、Web 系统 | 管道:ETL、日志、编译;分层:CRUD 业务系统 |
2. 调用/返回风格(典型例子:Web 三层、嵌入式主循环)
2.1 主程序/子程序(典型例子:main 调度脚本)
定义: 系统由一个主程序和若干子程序组成,主程序通过调用子程序来完成功能,子程序可以继续调用更底层的子程序。
直觉与适用: main → read_file → parse → print_report ,控制流是一棵调用树 ,数据靠参数与返回值往下传;嵌入式里 主循环 + 若干功能函数 同理。教材里结构化程序、教学与小工具 常用这种风格;适用 :算法题、固件、脚本工具链等流程相对固定的系统。
特点: 层次分解、自顶向下、顺序执行
优点:
- 读代码路径直 :从
main往下跟即可,适合新人维护线性脚本式工具。 - 调试简单 :断点打在调用栈上,调用链一目了然。
缺点:
- 扩展要改调用方 :新能力往往要动
main或上层分支,开闭原则上偏僵硬。 - 横切能力难插 :日志、鉴权、重试等要重复写或靠全局变量,易长成泥球。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 主程序-子程序、自顶向下调用树 | 嵌入式 main+中断服务、脚本式工具链、早期 C 项目 main 调工具函数 |
2.2 面向对象(典型例子:订单领域模型)
定义: 系统由一组对象构成,每个对象封装了数据和操作,对象之间通过消息/方法调用交互。
直觉与适用:
- 订单 算价、库存 扣减、支付 调三方------各自封装状态,对外只有方法;
- GUI 里按钮/窗口也是对象协作。
教材说大部分信息系统 可用 OO 组织;适用 :业务规则多变、需替换实现(策略/多态)的领域。
特点: 封装、继承、多态
优点:
- 改实现少改调用方 :通过接口与多态扩展,契合业务分支频繁变的场景。
- 领域模型可长期演进 :聚合、值对象等与业务语言对齐,便于需求沟通。
缺点:
- 调用链变深 :从 Controller 一路点到 Repository,排查问题要跨多个对象。
- 性能与边界 :细粒度对象 + 反射/动态分发可能带来额外开销 ;滥用继承会导致层次僵化。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 对象封装数据与行为、消息/方法调用、继承多态 | Spring 管理的 @Service/@Repository 、DDD 聚合根、UI 控件对象树 |
2.3 分层架构(Layers,典型例子:Controller-Service-DAO)
定义: 将系统划分为若干层次,每层只能调用相邻下层的服务,层与层之间通过接口通信。
直觉与适用:
- 展示→业务→数据 ,上层只依赖下层提供的服务 ,不直接翻库。工程上典型 Controller → Service → DAO → DB,答卷写「表现 / 业务 / 持久化」。
- 教材中企业 Web、ERP、操作系统分层 均属此类;适用 :请求路径清晰、团队按层分工的业务系统。
典型结构:
表示层(UI)
↓
业务逻辑层
↓
数据访问层
↓
数据库
特点:
- 每层职责单一
- 层间通过接口隔离
- 严格分层:只能调用直接下层
- 松散分层:允许跨层调用
优点:
- 分工与边界清楚 :前端/业务/数据并行开发,接口文档即契约。
- 替换成本低 :换 ORM、换 UI 技术栈,往往只动一层。
- 单测友好 :业务层可Mock 仓储,不启真库。
Mock 是啥(记一句): 单元测试里用假对象 顶替真实的 DAO/数据库访问:接口还在,但实现是「按测试需要返回写死的数据 」,这样只验证本层业务规则 对不对,不用起库、不用造库表数据。
缺点:
- 简单查询也要穿多层 :只查一个字段时,仍可能经历完整调用栈,延迟 与样板代码多(实践中常用 CQRS/读模型 或允许松散跨层缓解)。
若机械执行 「每层只做本层事」,连「查个用户名展示」也要 Controller→Service→Repository→DB ,每一跳都有对象组装、DTO 转换,简单事变啰嗦、耗时。
-
CQRS :把写(改状态)和 读(查展示)分开;读可走专门的读模型 (如宽表、物化视图、ES),甚至缓存直出 ,少经过核心业务层。读模型:为查询优化的一份「已算好的数据形态」,专供列表/报表。
-
松散跨层 :工程上允许简单只读 在规范下少绕几层(例如只读接口直达查询层),但要团队约定,避免变乱。
-
接口变更连锁:DTO、Facade 一层层同步改,版本管理成本高。
-
高实时、硬延迟 :全链路同步调用,不如专用路径(缓存直读、内核旁路)极致。
「硬延迟」指业务要求毫秒级甚至更低的响应(撮合、风控、实时游戏等)。
- 分层 里每一跳都是同步调用 + 对象转换 ,累计起来容易到不了极限。专用路径 =为热点单独开短路 :例如 缓存直读 ------Redis 命中则不再进 >Service/DAO,直接回包;
- 内核旁路 (常考提一句即可)指用户态直接处理网卡 (如 DPDK),少用户态↔内核态切换 和协议栈拷贝,把网络延迟压到极低。这类优化往往违背「整齐分层」 ,要在架构上显式标注热点路径。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 层次依赖、相邻层接口、严格/松散分层 | Controller→Service→Mapper 、DDD 接口与实现分包 、整洁架构 洋葱圈;性能热点常绕过层(读缓存、CQRS) |
3. 独立构件风格(典型例子:微服务、消息队列系统)
3.1 进程通信(典型例子:微服务 RPC 调用)
定义: 系统由多个独立运行的进程构成,进程之间通过消息传递通信。
直觉与适用: 两个进程不共享地址空间 ,订单通知库存只能走 HTTP/gRPC/MQ ,像两部门只通过工单 对齐。教材中分布式系统、微服务 是典型的进程通信风格;适用 :需要故障隔离、独立伸缩、语言/运行时不同的服务协作。
特点: 进程独立运行、通过消息交换数据
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 多进程、消息传递、无共享内存 | gRPC/HTTP 同步 、MQ 异步 、Actor 模型(Akka)、K8s 多容器协作 |
3.2 事件驱动 / 隐式调用(典型例子:支付成功发 MQ)
定义: 构件不直接调用彼此,而是通过发布事件来触发其他构件的响应。一个构件发布事件,系统自动将事件传递给所有注册了该事件的构件。
直觉与适用:
- 公众号发文 不必
@每个订阅者; - GUI (桌面/浏览器)里按钮只抛
click/onClick,谁监听由框架分发; - 后端 里订单支付成功只往 MQ(如 Kafka
order-paidtopic) 丢一条事实,积分服务、通知服务、对账任务 各自订阅消费------发布者不知道有几个订阅方、用啥语言写的。
典型落地:
- 同进程 :浏览器
CustomEvent/addEventListener,NodeEventEmitter,AndroidLiveData/观察者,都是「注册---派发---多监听」。 - 跨服务 :RabbitMQ exchange 绑定多队列 、RocketMQ 按 tag 订阅 、Webhook(上游 HTTP 回调一次,己方再分发给内部多个处理任务)等。
- 何时用 :用户注册成功 后要发短信、发券、写推荐侧画像------主链路只落库,允许晚几秒 ;设备告警 一条进总线,短信、大屏、工单 各订一份;促销零点突发下单 ,用队列先接住再慢慢消费,避免拖垮核心库。
- 扩容 :同一类事件多加消费者进程/实例分担积压(分区、顺序、幂等仍要设计)。
特点:
- 发布者不知道谁会处理事件
- 接收者不知道事件从哪里来
- 松耦合
优点:
- 扩功能少改核心 :新监听器/消费者独立上线,老流程不用大改。
- 削峰填谷 :用队列把突发流量异步消化,保护核心写库路径。
缺点:
- 全链路难一眼看清 :排查要依赖分布式追踪、消息轨迹,比同步 RPC 费神。
- 顺序与幂等 :同一实体的多个事件到达顺序 不确定,要设计幂等键、分区键、重试。
- 性能难估 :队列堆积、消费者滞后会导致尾延迟 不可控,需监控与背压。
考试高频关联: 发布-订阅模式属于事件驱动的典型实现。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 发布-订阅、隐式调用、松耦合 | Kafka topic + 多消费者组 、Spring @EventListener 、Outbox 表发事件 、前端 EventEmitter 、CQRS 读模型投影 |
4. 虚拟机风格(典型例子:Lua 脚本、规则引擎)
4.1 解释器(典型例子:嵌入式 Lua 脚本)
定义: 系统在进程里内嵌一套 「解释引擎」 (可理解为一台很小的虚拟机 ),能够读入 用某种自定义语言 / DSL / 脚本 写好的程序或规则,边解析边执行 ,而不必先把整份脚本编译成宿主语言的 .class/.so 再链接。
人话: 宿主程序(C++/Java)像操作系统 ,里面再跑一个迷你运行时 :你改的是「脚本文件 / 配置里的表达式 」,宿主不用重新编译、不用发大版本 ,重启或热加载脚本就换行为。和「编译型 」对照:编译是先全文翻译成机器码再跑 ;解释器是带着语义树或字节码一步一步走,更灵活、更慢。
结构(教材四件套,对应卷面可写):
| 构件 | 通俗理解 | 举例 |
|---|---|---|
| 解释引擎 | 读入脚本、解析、执行的内核 | Lua 虚拟机、规则表达式求值器、Excel 计算引擎 |
| 被解释的程序/规则 | 用户或策划写的「外面那层」 | .lua 文件、工作流 YAML、单元格公式 |
| 内部表示 | 引擎手里的中间形态 | AST(语法树)、字节码、逆波兰栈 |
| 引擎当前状态 | 执行到哪、变量环境 | 栈帧、全局表、当前单元格上下文 |
直觉与适用:
游戏 里技能伤害、Buff 由 Lua/JSON 脚本 配;网关 里用 Lua 插件 改路由;Excel 里改公式立刻重算------变的都是「数据 + 可执行片段 」,宿主二进制不动。
教材把 脚本引擎、DSL、可视化工作流(节点连线生成脚本) 都归到解释器。适合 :规则/流程周更甚至日更 、要热更 、业务方要能自己改 (或低代码平台生成)的场景;不适合 :纳秒级 撮合、百万 QPS 纯计算核心------那里仍要 native / JIT / 预编译。
优点:
- 发版解耦 :改行为走配置/脚本发布流水线 ,不必跟主应用同一次发版绑死。
- 沙箱与权限 :引擎可关掉危险 API (删文件、任意 JNI),只开放白名单函数,比「让业务直接写插件 so」安全。
- 快速试错 :运营在预发环境 改脚本验证,回滚即恢复旧文件。
缺点:
- 性能天花板 :逐条解释 + 动态类型检查,同样逻辑通常慢于 手写 Java/C++;热点路径常见做法是 JIT(如 V8) 或 把脚本编译成字节码再跑,或核心仍用 native。
- 调试与排障 :堆栈里同时出现宿主帧 + 脚本帧 ,日志要关联脚本行号,比纯 Java 难查。
- 治理 :版本号、灰度、谁有权改生产脚本、审计要单独做,否则「一行脚本拖垮全站」。
案例题一句: 「系统内嵌虚拟机,解释执行自定义语言/DSL,行为随脚本变更」------落笔 解释器风格 ;若题干强调 if-then 规则表、专家系统 ,下一节 规则系统 更贴。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 解释器、自定义语言、动态脚本 | Python/Lua 嵌入 、GraalVM 、JSON/YAML 配工作流 、Excel 公式引擎 、游戏技能脚本、Nginx/OpenResty Lua |
4.2 基于规则的系统(典型例子:风控规则引擎)
定义: 可看作解释器风格的一种特化 :系统行为不由「任意脚本」描述,而由有限形式的规则集 (常见 if-then 、产生式规则、决策表)驱动。规则引擎 读入当前事实 (客户征信分、订单金额、设备温度等),做模式匹配 ,命中后执行动作 (拒贷、打折、告警)。语义仍由引擎解释,但语言形态 比通用 Lua/Python 更受约束,便于校验与审计。
人话: 业务方改的是一张**「条件 → 结果」表或规则包,而不是写过程式代码。引擎问:现在的事实满足哪几条前提? 满足的 按优先级/冲突策略执行。和上一节 通用解释器的差别:规则系统 强调「事实 + 规则库 + 推理/匹配 」;通用解释器可以跑任意循环、任意 API 调用**,自由度大、难自动分析冲突。
典型结构(卷面可简写三要素):
| 要素 | 含义 | 举例 |
|---|---|---|
| 事实 / 工作内存 | 当前上下文里为真的数据 | 「用户年龄=25」「订单金额=199」「风险标签=可疑」 |
| 规则 | 条件 → 动作 | 「若年龄小于 18 则拒绝」;「若金额大于 100 且为会员则减 20 元」 |
| 引擎 | 匹配、排序、执行、处理冲突 | Drools、自研决策树引擎、轻量 if-else 生成器 |
直觉与适用: 信贷/授信 :征信分、负债率命中则自动拒贷或转人工 ;营销 :满减、券叠加、会员日写成决策表 ,改表即改活动;风控 :实时特征(设备指纹、行为分)灌进规则集 ,命中则拦截、验证码、人工复核;审批 :「金额大于某阈值走部门经理」这类流程规则 也可规则化。教材中专家系统、业务规则引擎、决策流 均属此类。适合 :分支极多 、监管/内控 要求「每条决策可对条款 」、规则周更 且要留痕 的场景。不适合 :强依赖过程算法 (大量矩阵运算、图形学)且无法用 if-then 表达的核心------仍应用原生代码 或上一节的通用脚本。
优点:
- 可解释、可对条款 :一条规则往往直接对应合同/制度 里的一句话,合规审计好写。
- 业务参与度高 :产品/风控在配置台 改规则,比改 Java 门槛低(仍要评审与灰度)。
- 与解释器共性 :规则外置,主系统发版 与规则发布可分离。
缺点:
- 规则爆炸与冲突 :上千条规则时,两条同时命中却结论相反 怎么办?需要优先级、互斥组、测试矩阵,工程量大。
- 回归测试难 :改一条规则可能意外影响 另一场景,需要全量规则用例 或模糊测试。
- 性能 :朴素「扫全表规则」在高 QPS 下会爆;线上常用决策树编译 、索引、短路 ,或热点下部分规则下沉代码。
- 治理 :规则版本、灰度、回滚、谁审批上线与通用脚本同源问题。
与 4.1 边界: 题干写「脚本语言、DSL、虚拟机 」→ 偏 解释器 ;写「规则库、事实匹配、专家系统、决策表 」→ 偏 基于规则的系统 。二者常同现(规则引擎内部也是解释执行)。
案例题一句: 「行为由规则集 驱动,引擎根据当前数据 匹配规则」------落笔 基于规则的系统。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 规则集、事实匹配、解释器特化、产生式规则 | Drools 、Easy Rules 、自研决策表引擎 、风控策略中心 、营销规则台 、Camunda/BPMN 中带 DMN 、轻量 YAML 规则 |
5. 仓库风格(典型例子:共享数据库、Wiki 知识库)
5.1 数据库系统(典型例子:ERP 多模块共库)
定义: 多个构件(子系统、批处理任务、报表模块)不以远程 API 调用为主协作 ,而是以同一套(或强关联的)数据库 为事实来源(Source of Truth) :通过 INSERT/UPDATE/SELECT 在同一批表上接力写读 ,完成业务流程。教材称「仓库风格 里的数据库系统」------仓库即共享库,构件围绕它读写。
人话: 各团队不用约定「谁先调谁」,只要约定表名、主键、状态字段含义 :A 系统把订单写成「待发货」,B 系统轮询或触发器看到就发货------对齐靠行数据,不靠 HTTP 。这是集成成本最低 的一种方式,也是技术债最容易堆的一种。
典型协作长什么样:
子系统/批任务 A ──┐
子系统/批任务 B ──┼── 共享库(订单表、库存表、状态表...)
子系统/批任务 C ──┘
| 维度 | 考试可写 | 工程里常见 |
|---|---|---|
| 中枢 | 共享数据库、集中存储 | 单库多 schema,或一业务库 + 只读从库给报表 |
| 协作方式 | 构件通过读写库交互 | 轮询状态表 、定时任务扫表 、触发器(慎用)、消息 Outbox 也落库 |
| 一致性 | 单库事务 | 同一连接内 BEGIN...COMMIT;跨系统仍是一写多读 |
特点: 数据集中存储、构件通过数据库协作。
优点:
- 上手与集成极快 :会 SQL、有账号就能参与协作,不强制先设计 REST/版本化契约。
- 强一致在单库内好做 :多表更新包在一个事务里,对账往往直接 SQL 搞定。
- 报表与对账天然 :所有状态最终在库里,审计 、BI 直接抽数。
缺点:
- Schema 与锁成全局瓶颈 :加字段、改类型要协调所有读写方 ;热点行行锁会把无关模块拖慢。
- 隐式契约 :「谁允许改
status从 1 到 2」只靠约定 ,没有编译期检查,误更新难防。 - 演进困难 :想拆库、拆服务时,表被 N 个未知作业依赖 ,割接成本极高(大泥球典型来源)。
案例题一句: 「多个子系统通过共享数据库读写协作、以库为集成中枢 」------落笔 仓库风格---数据库系统 ;若题干是 AI 多模块往共享中间态写 ,改 黑板(见下节对比表)。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 共享库、多构件读写协作、以库为中枢 | 遗留多模块共库 、状态表驱动工作流 、夜间批处理改同一套结果表 、反模式但真实:集成靠 DB |
5.2 黑板系统(Blackboard,典型例子:语音识别)
定义: 由三部分组成:黑板 (共享数据区,存放当前中间假设/中间结果 )、知识源 (彼此独立的专家模块,各自擅长一种推理或一种特征)、控制器 (调度:谁先看黑板、谁允许写、何时停机)。各知识源不直接调用对方 ,只读黑板、写黑板 ;没有唯一固定的解题流水线 ,解是多轮迭代出来的。
人话: 像多人共写一块白板 :每人只根据自己看到的板书追加或修改 结论;主持人(控制器)决定「下一步谁发言」。和 5.1 数据库系统的差别:数据库多是业务事实 的权威存储;黑板多是求解过程中的中间态 (可能删改、可能推翻),强事务与审计 不是重点,迭代与组合是重点。
三要素(卷面可写):
| 构件 | 通俗理解 | 考试里常举 |
|---|---|---|
| 黑板 | 共享的中间结果容器 | 当前候选词、当前图、候选排序列表 |
| 知识源 | 独立专家,只认黑板协议 | 声学模型、语言模型、规则引擎、精排模型 |
| 控制器 | 调度与终止策略 | 谁下一轮、何时输出最终解、冲突仲裁 |
直觉与适用: 题干出现「多专家 / 多模块往共享区写中间结果、无固定顺序、迭代求精 」即黑板。教材多用于无唯一闭式算法 的复杂问题(识别、理解、多模态融合)。适合 :多路策略要协同 、中间态频繁变 ;不适合 :强 ACID 业务记账 、清晰 CRUD ------优先用 5.1 数据库 或 API。
结构:
知识源A ──┐
知识源B ──┼── 黑板(共享数据)←── 控制器
知识源C ──┘
优点:
- 异构算法可共存 :规则、统计、深度学习各写一块,不必先统一成一种范式。
- 加专家相对独立 :新模块主要对接黑板协议(读写字段约定),少改旧代码。
- 契合「解不出来就再试」 :适合搜索、推理、融合类问题。
缺点:
- 终止与调度难 :何时算「够好」、会不会死循环贡献 ,依赖控制器策略 与经验。
- 测试难复现 :中间态多、激活顺序不固定,自动化测试 成本高;解的质量无硬保证。
- 运维与可观测 :中间态多变,排障常要落盘某时刻黑板全貌(快照) 、按一次请求重放各知识源顺序(回放)------比普通 CRUD 更依赖这类能力,否则只能猜。
案例题一句: 「黑板 + 知识源 + 控制器 、多模块共享中间态 、迭代求解 」------落笔 黑板系统 ;若题干是 OLTP 订单库存 、强一致,勿与黑板混用。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 黑板共享区、知识源、控制器、迭代求精 | 语音识别/融合 、搜索多路召回→精排 、推荐多路融合、实验管线里「共享中间结果 JSON」 |
考试高频对比:黑板 vs 数据库
| 维度 | 黑板系统 | 数据库系统 | 工程差异(落地) |
|---|---|---|---|
| 触发方式 | 知识源主动观察黑板 | 应用主动查询数据库 | 黑板:多算法盯共享中间态 ;库:业务代码发 SQL |
| 交互模式 | 数据驱动 + 控制器协调 | 请求-响应 | 黑板:迭代求精 ;库:事务内读写 |
| 适用领域 | AI、信号处理 | 企业信息系统 | 黑板:语音/图像/推荐多路 ;库:OLTP 业务库 |
5.3 超文本系统(典型例子:在线文档系统)
定义: 信息组织成图 而非单线目录:基本单位是节点 (页面、文档、资源),节点之间用超链接 相连;用户沿链接跳转 浏览,路径不预先固定。教材把 Web、在线文档、部分 API 导航 归为此风格。
人话: 像维基 :从 A 词条点到 B,再点回 C,中心是「关联」而不是「科长→科员」树状汇报。和 5.1 数据库 (表行)、5.2 黑板(中间态)不同:超文本强调内容与内容之间的可导航关系 ,不一定有强事务。
和「目录树」对照:
| 树状目录 | 超文本 | |
|---|---|---|
| 结构 | 父子一层层 | 任意节点可互链,网状 |
| 典型 | 资源管理器左栏 | 网页、文档站、图谱 |
直觉与适用: 题干强调跳转、相关阅读、发现式浏览 即此类。教材常举 Web、在线帮助 ;工程上 SPA 路由 、OpenAPI 的 links 、HATEOAS 、知识图谱 都是「带链接的信息网」。适合 :百科、文档站、门户、以关联检索 为主的产品;不适合 :要求严格层级权限 且只能自上而下访问------更偏 树 + ACL 的产品设计。
优点:
- 灵活导航 :读者自选路径,利于探索与学习。
- 增量扩展 :新页面只挂链接进来,不必改整棵目录。
缺点:
- 迷路风险 :链接多若无面包屑/站点图,用户易迷失;需信息架构与搜索补位。
- 一致性维护 :死链、重命名、多语言同步改链接,要有工具链。
案例题一句: 「信息以节点 + 超链接 组织,网状跳转 」------落笔 超文本系统(仓库风格子类)。
双轨对照
| 考试(卷面) | 工程落地 |
|---|---|
| 节点、超链接、网状导航 | SPA 路由 、文档互链 、OpenAPI 相关资源链接 、知识图谱 、HATEOAS _links |