Skip to content
难度基础(★)
建议时长45分钟

10.5.5 行为型设计模式-02

这一节继续行为型模式,重点是几个高频易混项:状态、策略、模板方法、访问者,以及它们与观察者、桥接、中介者的区别。

状态模式:把状态封装成类

状态模式允许一个对象在内部状态改变时改变它的行为。它的核心做法是:把不同状态封装为不同状态类。

mermaid
classDiagram
  class Context {
    -State state
    +request()
    +setState(state)
  }
  class State {
    +handle()
  }
  class SoldOutState
  class HasMoneyState
  class SoldState
  Context --> State
  State <|.. SoldOutState
  State <|.. HasMoneyState
  State <|.. SoldState

课程用自动售货机场景说明:根据库存、存入金额、找零能力、所选项目等不同情况,系统会有不同状态和不同行为。类图中出现 State,并且每个具体类对应一个状态时,通常就是状态模式。

状态模式与 UML 状态图有联系:状态图中的状态节点,如果进一步设计成状态类,就进入了状态模式的实现层。

策略模式:把算法封装成类

策略模式定义一系列算法,把每个算法封装起来,使它们可以互相替换。策略变化不应影响使用策略的客户。

mermaid
classDiagram
  class Context {
    -Strategy strategy
    +calculate()
  }
  class Strategy {
    +algorithm()
  }
  class GoldDiscount
  class SilverDiscount
  class NoDiscount
  Context --> Strategy
  Strategy <|.. GoldDiscount
  Strategy <|.. SilverDiscount
  Strategy <|.. NoDiscount

状态和策略类图相似,但语义不同:

对比项状态模式策略模式
封装对象状态算法/方案
变化原因对象内部状态变化客户选择不同算法
行为变化状态变了,行为随之变算法替换,计算方式变
典型例子会员等级状态升级导致行为变化金卡折扣、银卡折扣、普通折扣算法

如果题干强调“内部状态改变时行为改变”,选状态;如果强调“一组算法可以互换”,选策略。

模板方法:父类定义算法骨架

模板方法在父类中定义算法骨架,把某些具体步骤延迟到子类实现。它不是把完整算法封装成可互换对象,而是规定流程框架,让子类填写变化步骤。

mermaid
classDiagram
  class AbstractClass {
    +templateMethod()
    #step1()
    #step2()
  }
  class ConcreteClassA
  class ConcreteClassB
  AbstractClass <|-- ConcreteClassA
  AbstractClass <|-- ConcreteClassB

课程用文档模板填空来记忆:合同模板的结构不变,空白处由不同人填写。模板方法的关键词是“算法骨架”“步骤延迟到子类”“不改变算法结构”。

对比项策略模式模板方法
变化方式通过组合替换算法对象通过继承重写部分步骤
固定内容客户持有策略接口父类固定算法骨架
关键词算法可互换算法骨架固定,步骤可变

访问者:数据与操作分离

访问者模式表示一个作用于对象结构中各元素的操作。它可以在不改变元素类的前提下,定义作用于这些元素的新操作。

课程强调两个判断点:

  • 访问者中常有 visit() 方法。
  • 元素中常有 accept(visitor) 方法。
mermaid
classDiagram
  class Visitor {
    +visitItemA(item)
    +visitItemB(item)
  }
  class CheckoutVisitor
  class Element {
    +accept(visitor)
  }
  class ItemA
  class ItemB
  class ObjectStructure {
    +accept(visitor)
  }
  Visitor <|.. CheckoutVisitor
  Element <|.. ItemA
  Element <|.. ItemB
  ObjectStructure o-- Element
  Element ..> Visitor : accept()

访问者适合对象结构相对稳定,但对这些对象要增加很多不同操作的场景。例如购物车里有多种商品元素,人工结账、自助结账、促销统计等是不同访问方式。访问方式选定后,会依次访问所有元素。

访问者与桥接、观察者、策略的区分

易混项为什么像真正区别
访问者 vs 桥接都可能有两个变化维度桥接把两个维度组合起来;访问者选择一种访问方式去遍历对象结构
访问者 vs 观察者名字都带“者”,都有对象关系观察者是一对多联动;访问者是数据与操作分离
访问者 vs 策略都有“选择一种方式”策略只封装算法;访问者要访问另一侧对象结构中的所有元素

访问者的强项是增加新操作方便;弱点是如果元素类频繁变化,就要改很多访问者。因此它适合“对象结构稳定、操作经常变化”的系统。

行为型真题判断层次

课程总结了设计模式判断的三个层次,在行为型题里尤其明显:

  1. 应用场景匹配:自动售货机状态变化、购物车结算、后端模型连接多个前端界面。
  2. 类名/方法名识别:StateCommandVisitoraccept()visit()
  3. 意图排除:观察者是一对多通知,备忘录是保存状态,命令是请求对象化,职责链是层层传递。

例如自动售货机题,如果类图里有 State,并且不同状态对应不同处理行为,就选状态模式;超市销售系统如果出现 accept() 并把商品条目与结账方式分离,就选访问者模式。

例题

单选
提供一种方法顺序访问聚合对象中的元素,而不暴露内部表示,属于:
单选
允许一个对象在内部状态改变时改变它的行为,属于:
单选
在不改变对象结构中元素类的前提下,为这些元素增加新的操作,适合使用:

自查要点

  1. 状态模式和策略模式为什么类图相似但意图不同?
  2. 模板方法和策略都涉及算法,区别在哪里?
  3. 访问者的 visit()accept() 分别在哪一侧?
  4. 为什么对象结构频繁变化时不适合访问者模式?