晚上七点,一家川菜馆正是最忙的时候。后厨里,师傅看了一眼点菜单上的“鱼香肉丝”——是炒、是蒸、还是少油?他根据今天食材的新鲜程度随手选了最合适的一种做法。客人只关心菜好不好吃,不关心锅里发生了什么。这是策略模式。 隔壁桌的客人喊“退菜”,服务员掏出小票,在旁边画了个叉,这个请求被写成一条退单消息,排队传回后厨。菜还没下锅,退得掉;已经下锅,就得按规则走退款流程。这张小票就是一条命令。 而如果客人不满意要投诉,服务员会先道歉安抚,安抚不了请领班,领班解决不了再请店长——一个“麻烦”沿着一条看不见的链条往上递,直到遇到一个能拍板的人。这是责任链模式。 三个故事放在一起看:它们都是“我提出需求,你完成动作”之间的一层缓冲,让发出请求的人不必认识真正干活的人。软件世界里,这三大行为型设计模式,正是用同样的智慧,把“谁请求”“怎么请求”“谁来干”拆得清清楚楚。这篇文章,带你在这家“代码川菜馆”里把三道招牌菜一次吃透。

岗位关联

掌握策略、命令、责任链三大行为型模式,是 AI 工程师写出可扩展、可维护系统的基本功——当模型路由、任务调度、审核流这些真实需求到来时,你能一眼看穿它们的骨架,用最少的代码承接最大的变化。

目录
  1. 可视化演示
  2. 行为模式定位
  3. 策略模式解析
  4. 命令模式解析
  5. 责任链模式解析
  6. 三模式对比
  7. 综合实战应用
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

行为模式定位

晚上七点,川菜馆后厨热火朝天。客人、服务员、厨师各忙各的,谁都不需要认识谁:客人对服务员说“加个菜”,服务员在小票上写两笔,厨师瞟一眼就开火。这座餐厅之所以能在高峰期不崩溃,靠的不是心灵相通,而是每两个人之间都隔着一张“纸”——点菜单、小票、投诉单。软件世界也一样,对象之间一旦“直接认识”,改动就会像多米诺骨牌一样蔓延。

设计模式中有一类“行为型模式”,专门研究“对象之间如何协作、职责如何分配”。策略、命令、责任链,是其中最常用的三张“纸”。它们的共性一眼就能看出来:都在请求发送者(客人)与请求接收者(厨师)之间插入一层抽象,把“谁请求”和“谁来干”拆开。

耦合的根源,是发送者对接收者“知道得太多”——知道具体类名、知道方法签名、知道执行顺序。知道得越多,牵一发动全身。三个模式的目标,就是让发送者只依赖抽象的接口,从而让接收者可以自由替换、扩展、重组。

三种模式对“传话”的理解各不相同:策略说“我选一个人去干”,命令说“我把这件事记下来,晚点干、可以撤”,责任链说“我挨个问,谁行谁来”。记住这三句话,后面三节逐一展开。

就像餐厅里“点菜单”“小票”“投诉单”三张纸各有各的格式,三种模式解决问题的角度也不同,但共同点始终没变——让传话的双方不必互相认识。

Coupling(C,R)=∣ {假设:C→R} ∣,目标: C 只依赖抽象 A\displaystyle \text{Coupling}(C, R) = |\, \{ \text{假设}: C \to R \} \,|, \qquad \text{目标: } C \text{ 只依赖抽象 } A
这个公式说:耦合度等于“假设的数量”。假设越少,两边越自由;而插入抽象层,就是剪断这些假设的绳子。
  1. 耦合度定义Coupling(C, R) 表示调用方 C 与接收方 R 的耦合程度,用 C 对 R 作出的“假设数量”来度量。
    Coupling(C,R)=∣ {假设:C→R} ∣\displaystyle \text{Coupling}(C, R) = |\, \{ \text{假设}: C \to R \} \,|
  2. 假设集合花括号里装着 C 对 R 的所有认知——具体类名、方法签名、内部流程,每一项都是一根绑在两者之间的绳子。
    假设∈{类型,签名,流程,状态}\displaystyle \text{假设} \in \{ \text{类型}, \text{签名}, \text{流程}, \text{状态} \}
  3. 抽象目标通过引入抽象 A,让 C 只认识 A,R 只是 A 的一种实现。绳子被剪断,两边各自自由。
    C→A←R\displaystyle C \to A \leftarrow R

策略模式解析

客人点了一道“鱼香肉丝”。后厨里,师傅扫了一眼食材:今天的肉偏老,那就多抓一下;灶上油温正好,直接爆炒。炒、蒸、少油版——三种做法,出锅都是“鱼香肉丝”。客人从头到尾只说了“要一份鱼香肉丝”,至于怎么做,是厨师的事。

这个场景就是策略模式:把“要做什么”和“怎么做”分开。软件里,算法经常有多种实现,比如排序有快排、归并、堆排序,支付有支付宝、微信、银行卡。策略模式把这些算法封装成互相独立、可以替换的“策略”,让上下文(厨师)在运行时按条件选一个。

策略模式有三个角色:策略接口定义算法合约,具体策略类各自实现一个算法,上下文持有策略引用并在需要时调用。它的直接收益是干掉一长串 if-else——新增算法只需加一个类,不改动上下文和调用方,这就是“开闭原则”:对扩展开放,对修改关闭。

点菜单上永远写着“鱼香肉丝”四个字,但今天端上来的可能是爆炒、可能是蒸菜。菜单不用改,厨师不用换,变的只是后厨里那个“策略”——这就是策略模式对客户端的承诺:接口不变,算法随便换。

Context→S∈{S1,S2,…,Sn},execute(x)=S.algo(x)\displaystyle \text{Context} \to S \in \{ S_1, S_2, \ldots, S_n \}, \qquad \text{execute}(x) = S.\text{algo}(x)
上下文只认识策略接口,具体是哪个算法在执行,由运行时决定——就像客人只认识“鱼香肉丝”这个菜名,不关心后厨用哪口锅。
  1. 上下文持有策略Context 只持有一个策略接口 S 的引用,具体是哪个实现,由运行时决定。
    Context→S\displaystyle \text{Context} \to S
  2. 策略集合所有策略实现同一接口,组成一个可互换的算法家族,随时可以扩充新成员。
    S∈{S1,S2,…,Sn}\displaystyle S \in \{ S_1, S_2, \ldots, S_n \}
  3. 多态调用调用时通过多态分派到具体算法,同一行代码在不同策略下产生不同行为。
    execute(x)=S.algo(x)\displaystyle \text{execute}(x) = S.\text{algo}(x)

命令模式解析

客人喊“服务员,退菜!”。服务员没有直接冲进后厨把锅掀了,而是掏出小票,写下“退:3号桌水煮鱼一条”,传到后厨。师傅看了小票,菜还没下锅,照办;已经下锅,按退款规则处理。整个过程,“退菜”这个请求变成了一张可以传递、可以存档、可以撤销的纸。

命令模式做的就是这件“把小票物化”的事:把一次请求封装成一个独立对象。这个对象里装着“让谁干、干什么、参数是什么”。于是,请求不再是一句飘在空中的话,而是一个可以排队、可以记录、可以反悔的实体。

命令模式有四个角色:Command 接口定义 execute 和 undo,具体命令持有接收者引用和参数,接收者真正干活,调用者只负责触发命令。因为命令是对象,我们可以把它塞进队列延迟执行、放进历史栈支持撤销重做、写成日志做审计——这些能力是普通函数调用给不了的。

小票上的每一笔,可排队、可退单、可查账。命令对象就是代码里的“小票”——把“请帮我把这件事做了”从一句话,变成一件可以放在口袋里、随时掏出来的东西。

Invoker→setCommandCommand→executeReceiver\displaystyle \text{Invoker} \xrightarrow{\text{setCommand}} \text{Command} \xrightarrow{\text{execute}} \text{Receiver}
undo:Command→Receiver.restore(state)\displaystyle \text{undo}: \text{Command} \to \text{Receiver}.\text{restore}(\text{state})
调用者按按钮,命令对象指挥接收者干活;撤销时,命令拿着保存好的状态让接收者反向操作——就像按小票退菜。
  1. 调用者登记命令Invoker 只认识 Command 接口,按一下按钮就是调一次 execute,不知道底层是谁在干活。
    Invoker→setCommandCommand\displaystyle \text{Invoker} \xrightarrow{\text{setCommand}} \text{Command}
  2. 命令指挥接收者execute 内部协调 Receiver 完成真实动作,参数与接收者都封在命令里。
    Command→executeReceiver\displaystyle \text{Command} \xrightarrow{\text{execute}} \text{Receiver}
  3. 撤销即恢复undo 利用命令执行前保存的状态,让接收者反向操作,实现可逆。
    undo:Receiver.restore(state)\displaystyle \text{undo}: \text{Receiver}.\text{restore}(\text{state})

责任链模式解析

客人对菜不满意,拍桌子要投诉。服务员先迎上去:“我帮您重做一份”;客人不接受,服务员请来领班;领班做不了主,又请店长。这个“麻烦”沿着一条看不见的链子往上走,走到哪一级,哪一级先判断“我处理得了吗”,处理不了就继续往上递。

责任链模式正是这条“投诉链”:把多个处理者串成一条链,请求从链头进入,逐个经过处理者。每个处理者自行判断能不能处理,能则处理并终止,不能则传给下一个。请求的发送者只需要面对链头,完全不知道后面还有谁。

责任链的实现核心是“后继引用”:每个处理者持有 next 指针,handle 方法先调用 canHandle 判断,处理不了就调用 next.handle。链的组装方式很灵活——可以在运行时按条件动态拼接,也可以在中间插入或摘除节点。终止条件完全由业务决定:可以“处理即止”,也可以“全部探一遍”。

客人永远不需要知道店长坐在哪间办公室,他只要对着最近的服务员喊一嗓子。责任链的价值就在这:请求者只认识第一个节点,后面有多少级、谁最终拍板,统统与你无关。

H1→H2→⋯→Hn\displaystyle \text{H}_1 \to \text{H}_2 \to \cdots \to \text{H}_n
Hi.handle(r)={处理并结束,若 canHandle(Hi,r)Hi+1.handle(r),否则\displaystyle \text{H}_i.\text{handle}(r) = \begin{cases} \text{处理并结束}, & \text{若 } \text{canHandle}(H_i, r) \\ \text{H}_{i+1}.\text{handle}(r), & \text{否则} \end{cases}
每个节点先问自己“我能处理吗”,能就接下,不能就传给下一个——这就是投诉从服务员一路升到店长的数学表达。
  1. 链式结构每个处理者持有下一个处理者的引用,形成单向链,决定请求的走向。
    H1→H2→⋯→Hn\displaystyle \text{H}_1 \to \text{H}_2 \to \cdots \to \text{H}_n
  2. 能力自判每个节点先问自己“我处理得了吗”,这是整个模式的原动力。
    canHandle(Hi,r)\displaystyle \text{canHandle}(H_i, r)
  3. 逐级传递处理不了就调用后继者的 handle,把请求往下传,直到有人接手或走到链尾。
    Hi+1.handle(r)\displaystyle \text{H}_{i+1}.\text{handle}(r)

三模式对比

同一家川菜馆,三张纸各管一摊:点菜单告诉厨房“做什么菜”,小票记录“谁点了什么、要不要退”,投诉单决定“这个麻烦该谁拍板”。把三张纸混着用,后厨立刻乱成一锅粥。

软件里也一样。三个模式都解耦,但解耦的方向完全不同:策略是“选择一个算法”,命令是“封装一个请求”,责任链是“传递一个请求”。用错场景,代码不是更清晰,而是更绕。

看三个维度。解耦方向上:策略把“用哪个算法”的决策权交给上下文;命令把“何时执行、能否回滚”的控制权交给队列和历史栈;责任链把“谁该处理”的判断权下放给每个节点。扩展方式上:策略加一个类,命令加一个命令对象,责任链往链尾挂一个节点,三者都满足开闭原则。调用时序上:策略一次调用就结束,命令延迟到任意时刻、可反复调度,责任链可能经过多个节点才终止。

三张纸不能混着用:让点菜单去处理投诉,客人会被气走;让投诉单去传菜,师傅直接懵。挑对模式,餐厅才转得起来。

策略:请求→选 1 个算法命令:请求→对象→延迟执行\displaystyle \text{策略}: \text{请求} \to \text{选 } 1 \text{ 个算法} \qquad \text{命令}: \text{请求} \to \text{对象} \to \text{延迟执行}
责任链:请求→N 个处理者依次尝试,直至命中\displaystyle \text{责任链}: \text{请求} \to N \text{ 个处理者依次尝试,直至命中}
一句话记住区别:策略是“挑一个”,命令是“存下来”,责任链是“传下去”。
  1. 策略的选择关系一个请求从算法集合里选一个来执行,强调运行时替换。
    请求→选 1 个算法\displaystyle \text{请求} \to \text{选 } 1 \text{ 个算法}
  2. 命令的封装关系请求先被包装成对象,再决定何时执行、是否撤销。
    请求→对象→延迟执行\displaystyle \text{请求} \to \text{对象} \to \text{延迟执行}
  3. 责任链的传递关系一个请求在多个处理者之间流转,直到某个节点愿意接手。
    请求→N 个处理者依次尝试\displaystyle \text{请求} \to N \text{ 个处理者依次尝试}

综合实战应用

成熟的餐厅,三张纸是协同作战的:客人点菜,点菜单(命令)排队传进后厨;后厨按食材状态选做法(策略);一旦菜品出问题,投诉沿着服务员到店长往上走(责任链)。一套流程严丝合缝。

真实系统同样三管齐下。以 AI 平台的支付系统为例:用户下单,支付请求被封装成命令对象进入队列,按序执行、支持退款撤销;支付渠道(扫码、银行卡、余额)用策略封装,按金额和场景动态选择;风控环节用责任链串起“限额检查、欺诈识别、人工审批”,逐级拦截。

看一张协作图:下单模块生成 PayCommand 入队,队列调度器触发 execute;命令内部把渠道选择委托给策略,策略按金额选合适的支付方式;执行前,命令先经过风控链,链上任何一级拦截,命令都不执行并返回失败。三个模式各管一段,互不侵入——新增一个支付渠道,只需加一个策略类;新增一条风控规则,只需往链尾挂一个节点;新增一个退款场景,只需加一个命令。

这张协作图就是“代码川菜馆”的后厨全景:点菜单、厨师、投诉机制各司其职又互相咬合。把三个模式组合起来用,系统才能既扛得住流量,又经得起改需求。

PayCommand(order)→queue策略(channel)→审核链(limit→fraud→manual)\displaystyle \text{PayCommand}(\text{order}) \xrightarrow{\text{queue}} \text{策略}(\text{channel}) \xrightarrow{\text{审核}} \text{链}(\text{limit} \to \text{fraud} \to \text{manual})
AI 平台计费扣款、Agent 工具调用、多级内容审核,都和这张图同构:命令排队,策略路由,责任链把关。
  1. 命令入队支付请求对象化为 PayCommand,进入队列延迟执行,并保留撤销能力。
    PayCommand(order)→queue\displaystyle \text{PayCommand}(\text{order}) \xrightarrow{\text{queue}}
  2. 策略选渠道命令内部委托策略对象选择支付渠道,渠道可替换、可扩展。
    策略(channel)\displaystyle \text{策略}(\text{channel})
  3. 责任链审核执行前经过风控链,逐级检查,任何一级拦截则整个请求终止。
    链(limit→fraud→manual)\displaystyle \text{链}(\text{limit} \to \text{fraud} \to \text{manual})

AI 应用场景

LLM 模型路由与降级

阶段:模型调用与推理

解决的问题:单一模型供应商可能超时、限流、价格波动。传统 if-else 硬编码切换模型不可维护,且每次新增供应商都要改动核心逻辑。用策略模式把 OpenAI、Claude、本地模型封装为可互换的策略,按成本、延迟、任务难度动态选择,供应商故障时自动降级到备用策略。

具体案例:RAG 问答系统按问题复杂度路由——简单问题走轻量模型省钱,复杂推理走旗舰模型保证质量;当旗舰模型限流时,系统自动切换到备用策略,业务代码一行不动。

Agent 工具调用队列

阶段:Agent 规划与执行

解决的问题:Agent 产生的工具调用(查库存、发消息、改配置)需要排队、失败重试、出错回滚。命令模式把“调用工具”封装成对象,便于记录执行历史、支持 undo;调用失败时可以通过命令的逆操作回滚,而不是让系统停留在中间状态。

具体案例:一个自动化运营 Agent 依次执行“查库存、下单、发通知”,第三步失败时,系统遍历历史命令栈,对已执行的命令逐个 undo,把库存和订单状态恢复到操作前。

多级内容审核

阶段:安全与合规

解决的问题:单一拦截器覆盖不了所有风险。责任链把“关键词过滤、AI 风险分级、人工复核”串成一条链:先跑敏感词库(快),命中高危再交给审核模型,模型置信度低再转人工。每一级只做自己的判断,加新策略只需往链尾追加一个处理器。

具体案例:UGC 平台的内容审核管线——词表命中直接拦截,疑似内容交给审核模型打分,得分在临界区的内容进入人工队列。高峰期通过动态调整链上节点顺序,优先过审低风险内容。

Python 演示

python81 行
"""策略 + 命令 + 责任链: AI 平台订单支付与风控演示"""from abc import ABC, abstractmethod  class PayStrategy(ABC):    """策略接口: 所有支付方式的共同契约"""     @abstractmethod    def pay(self, amount):        """执行扣款"""  class CardPay(PayStrategy):    def pay(self, amount):        print("[银行卡] 扣款 {} 元".format(amount))  class QrPay(PayStrategy):    def pay(self, amount):        print("[扫码支付] 扣款 {} 元".format(amount))  class PayCommand:    """命令对象: 把支付请求封装成可排队, 可撤销的小票"""     def __init__(self, strategy, amount):        self.strategy = strategy        self.amount = amount        self.paid = False     def execute(self):        self.strategy.pay(self.amount)        self.paid = True     def undo(self):        if self.paid:            print("[退款] 返还 {} 元".format(self.amount))            self.paid = False  class RiskHandler:    """责任链节点: 每个处理者自行判断能否处理"""     def __init__(self, name):        self.name = name        self.next = None     def set_next(self, handler):        self.next = handler        return handler     def handle(self, amount):        if self.check(amount):            print("[{}] 通过".format(self.name))            return self.next.handle(amount) if self.next else True        print("[{}] 拦截".format(self.name))        return False     def check(self, amount):        return True  class LimitHandler(RiskHandler):    def check(self, amount):        return amount <= 5000  class FraudHandler(RiskHandler):    def check(self, amount):        return amount <= 20000  if __name__ == "__main__":    cmd = PayCommand(QrPay(), 3000)    cmd.execute()     chain = LimitHandler("限额检查")    chain.set_next(FraudHandler("欺诈识别"))    print("风控结果: {}".format(chain.handle(3000)))     cmd.undo()
目的:一个可运行的支付与风控演示,把策略(支付渠道)、命令(支付请求封装与撤销)、责任链(多级风控)三个模式串进同一段业务流。
  1. class PayStrategy(ABC):定义策略抽象接口,相当于菜单上固定的“菜名”,所有支付方式都实现它。
  2. class PayCommand:命令对象,把“扫码付 3000 元”封装成一张小票,可延后执行、可撤销。
  3. self.strategy = strategy命令内部持有策略引用,执行时把扣款动作委托给具体支付渠道。
  4. def undo(self):撤销刚才的执行,就像服务员按小票退菜。
  5. def set_next(self, handler):把责任链节点串起来,类似服务员指向领班、领班指向店长。
  6. return self.next.handle(amount) if self.next else True自己处理不了就传给下一个节点,走到链尾则放行。
  7. cmd = PayCommand(QrPay(), 3000)组装:命令 + 策略 + 金额,三者互不粘连。

常见误区

误区我理解了,策略模式就是把 if-else 改写成接口加几个类,本质没区别,还多了一堆文件,纯属过度设计。

正解多态只是实现手段,策略模式的本质是“定义算法家族 + 运行时替换 + 上下文与算法解耦”。真正的价值在于:新算法可以独立于上下文新增(开闭原则),策略可以被复用、单独测试、动态组合。比起 if-else,它消除了“改一个分支就要回归一片”的脆弱性。就像餐馆新增“蒸鱼”做法,不需要修改点菜单和客人,只加一道工艺。

为何常见:初学阶段看到的多是“用策略替换 if-else”的改造案例,很容易把实现手段当成模式本身,看不到耦合度下降带来的长期收益。

误区命令模式不就是把函数当参数传吗?Python 里一个 lambda 就搞定了,何必专门建一个类。

正解函数引用只封装了“要执行的函数”,而命令对象还能携带接收者、参数、历史状态,并提供 undo 和 redo。lambda 只能“执行”,无法自然地保存“上一个状态”、无法批量排队、无法序列化到日志。小票和“口传一句话”的区别就在这里:口传会忘,小票可以入档、撤销、重打。

为何常见:现代语言里函数是一等公民,很多人看到命令的 execute 就觉得和回调一样,忽略了对象带来的状态保存与补偿能力。

误区责任链就是把处理者串成一个链表,从头到尾遍历一遍而已,数据结构课早就学过了。

正解链表是数据结构,责任链是协作模式。重点在于“每个节点自行决定是否处理、是否继续传递”,链路可以运行时动态组装,终止条件由业务规则决定,甚至可以在中间插入节点。它不是简单的遍历,而是把决策权下放给每个节点——就像投诉不一定会走到店长,服务员可能当场就解决了。

为何常见:责任链的实现确实用了 next 引用,形式上长得像链表,容易让人只看到结构而忽略“谁决策、何时终止”的协作语义。

误区反正都是解耦,我在一个场景里随便用哪个都行,效果差不多。

正解三者的意图完全不同:策略解决“从多个算法中选一个执行”,命令解决“把请求延迟化、队列化、可撤销”,责任链解决“多个处理者依次尝试”。选错模式会让代码语义混乱。比如支付渠道切换是策略问题,用责任链的话,每次都要让所有渠道“试一遍”才能找到对的,既低效又奇怪。

为何常见:三个模式都属于行为型且都引入了间接层,不深入理解各自意图,很容易“看着都像解耦”就混着用了。

面试题

Q1. 策略模式解决的核心问题是什么?

单选题 · easy

  • A.将请求封装成对象以支持撤销
  • B.让算法族可以独立替换变化← 正确答案
  • C.让多个处理者依次处理请求
  • D.用单例限制对象全局唯一

答案:B。策略模式的核心是“定义算法家族并让它们可互换”,让算法可以独立于客户端变化。A 是命令模式,C 是责任链模式,D 是单例模式。就像“鱼香肉丝”的做法可以换,但菜名不变。

Q2. 命令模式中真正执行业务动作的是谁?

单选题 · easy

  • A.调用者对象
  • B.命令对象自身
  • C.接收者对象← 正确答案
  • D.客户端程序

答案:C。命令对象负责组织和调度,但真正的业务逻辑由接收者(Receiver)执行。调用者只负责触发命令,客户端只负责组装。就像小票传递了“退菜”的请求,真正动手的是后厨师傅。

Q3. 责任链中,请求如何处理终止?

单选题 · easy

  • A.只能由链首节点决定
  • B.默认必须走完整条链路
  • C.由节点自主判断处理与否← 正确答案
  • D.处理结果必须全部一致

答案:C。责任链的每个节点自行判断“能否处理”,能处理就终止并返回,不能处理才传给下一个节点。链首只是入口,不是决策者。就像服务员能安抚的投诉,到不了店长那里。

Q4. 哪些场景适合使用命令模式?

多选题 · mid

  • A.实现操作的撤销与重做← 正确答案
  • B.把任务放入队列延迟执行← 正确答案
  • C.运行时替换支付渠道算法
  • D.记录操作日志便于审计← 正确答案

答案:ABD。命令对象可以入历史栈实现撤销重做(A),可以入队延迟执行(B),可以序列化到日志做审计(D)。C 是策略模式的典型场景——命令模式的重点是“请求物化”,不是“算法替换”。

Q5. 支付系统按金额选择合适的支付渠道,最适合用哪种模式?

单选题 · mid

  • A.命令模式
  • B.责任链模式
  • C.策略模式← 正确答案
  • D.观察者模式

答案:C。按条件从多个渠道算法中选一个执行,正是策略模式“运行时选择算法”的典型场景。命令模式管排队撤销,责任链管逐级处理,观察者管事件通知。

Q6. 关于责任链和策略模式,说法正确的有哪些?

多选题 · mid

  • A.策略是选择单个算法执行← 正确答案
  • B.责任链允许多节点依次尝试← 正确答案
  • C.策略模式原生支持撤销操作
  • D.责任链可动态调整节点顺序← 正确答案

答案:ABD。策略是“从多个算法中选一个”(A 对),责任链是“多个处理者依次尝试直到命中”(B 对),且链可以动态组装、调整顺序(D 对)。撤销是命令模式的能力,策略模式不原生支持(C 错)。

Q7. 命令模式实现撤销通常依赖什么?

单选题 · hard

  • A.命令内保存恢复所需状态← 正确答案
  • B.删除接收者的全部执行记录
  • C.重启系统恢复到初始状态
  • D.让策略模式代管回滚逻辑

答案:A。命令在 execute 前保存恢复所需的状态,undo 时用这些状态让接收者反向操作。B 破坏数据,C 不现实,D 混淆了模式职责。就像退菜要看小票上记录的原菜单信息,而不是把厨房砸了重来。

Q8. 审批流中,不同类型单据走不同审核路径,最适合用哪种模式?

单选题 · hard

  • A.策略模式
  • B.命令模式
  • C.责任链模式← 正确答案
  • D.建造者模式

答案:C。审批流是“请求沿多条路径逐级尝试处理”,普通单到部门经理即止,大额单继续往上走——这正是责任链“节点自判 + 逐级传递”的结构。策略模式擅长选算法而非走流程。

Q9. 三个行为模式共同带来的价值有哪些?

多选题 · mid

  • A.降低发送者与接收者耦合度← 正确答案
  • B.让新增行为满足开闭原则← 正确答案
  • C.加速算法本身的执行速度
  • D.提升代码可测试与可维护性← 正确答案

答案:ABD。三者都在发送者与接收者之间插入抽象,降低耦合(A);新增策略、命令、处理者都无需修改已有代码,满足开闭原则(B);独立封装让每个变体可以单独测试、替换(D)。它们不改变算法的时间复杂度,C 错。

Q10. 关于三种模式协作组合方式,正确的是?

单选题 · extreme

  • A.命令封装请求再交由策略执行← 正确答案
  • B.策略直接替代命令完成请求排队
  • C.责任链替代策略完成算法选择
  • D.三模式职责相同可任选其一

答案:A。真实系统里三者可以协作:命令把请求对象化(入队、撤销),策略负责从多个算法中选一个执行,责任链负责逐级审核传递。B、C、D 都把模式职责混淆了——排队撤销是命令的事,算法选择是策略的事,逐级处理是责任链的事,三者不可互相替代。