设计模式之动作型

设计模式之动作型

大家好,这里是编程Cookbook。本文是对设计模式中创建模式的详细讲解,共11种,分别是观察者模式、责任链模式、策略模式、模板方法模式、状态模式、迭代器模式、备忘录模式、命令模式、中介者模式、访问者模式、解释器模式。

常用动作型模式

观察者模式(Observer Pattern)

观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。观察者模式的核心思想是解耦观察者与被观察者,使得它们可以独立变化。

组成成分

观察者模式通常包含以下角色:

  1. 主题(Subject/Topic)

    • 维护一个观察者列表,并提供注册、删除和通知观察者的方法。
  2. 具体主题(Concrete Subject)

    • 实现主题接口,存储具体状态,并在状态改变时通知观察者。
  3. 观察者(Observer)

    • 定义一个更新接口,用于在主题状态改变时接收通知
  4. 具体观察者(Concrete Observer)

    • 实现观察者接口,在接收到通知时更新自身状态。

特点

优点

  1. 解耦:观察者与被观察者之间松耦合,可以独立变化。
  2. 动态关系:可以在运行时动态添加或删除观察者。
  3. 广播通信:支持一对多的通信方式,一个主题可以通知多个观察者。

缺点

  1. 性能问题:如果观察者过多,通知所有观察者可能会导致性能问题。
  2. 循环依赖:观察者与被观察者之间可能会出现循环依赖,导致系统复杂性增加。

使用场景

  1. 事件驱动系统

    • 当需要实现事件驱动系统时,可以使用观察者模式。
  2. 发布-订阅系统

    • 当需要实现发布-订阅系统时,可以使用观察者模式。
  3. GUI 框架

    • 当需要实现 GUI 框架中的事件处理机制时,可以使用观察者模式(如点击按钮,关闭弹窗等)。
  4. 状态监控

    • 当需要监控某个对象的状态变化时,可以使用观察者模式。

例子

  • 场景:你正在开发一个新闻发布系统,当有新的新闻发布时,所有订阅者(观察者)都会收到通知。
  • 解决方案:使用观察者模式,新闻发布者(主题)维护一个观察者列表,当新闻发布时,通知所有观察者。
  • 应用:消息推送、事件监听、股票行情监测等。

总结

  • 观察者模式:用于实现对象之间的一对多依赖关系,适用于事件驱动系统、发布-订阅系统等场景,解决了对象之间的通信问题。

责任链模式(Chain of Responsibility Pattern)

责任链模式是一种行为型设计模式,它允许多个对象有机会处理请求,从而避免请求的发送者与接收者之间的耦合。责任链模式的核心思想是将请求的发送者和接收者解耦,并将多个对象连成一条链,请求沿着链传递,直到有对象处理它为止


组成成分

责任链模式通常包含以下角色:

  1. 处理器接口(Handler)

    • 定义处理请求的接口,通常包含一个处理方法和一个设置下一个处理器的方法
  2. 具体处理器(Concrete Handler)

    • 实现处理器接口,负责处理请求。如果自己不能处理,则将请求传递给下一个处理器
  3. 客户端(Client)

    • 创建责任链,并将请求发送给链中的第一个处理器

特点

优点

  1. 解耦:请求的发送者和接收者之间解耦,发送者不需要知道具体的处理者。
  2. 灵活性:可以动态地添加或修改处理链。
  3. 可扩展性:可以很容易地增加新的处理者。

缺点

  1. 性能问题:如果责任链过长,可能会导致性能问题。
  2. 请求可能未被处理:如果链中没有处理者能够处理请求,请求可能会被忽略。

使用场景

  1. 多级审批

    • 当需要实现多级审批流程时,可以使用责任链模式。
  2. 事件处理

    • 当需要处理多个事件处理器时,可以使用责任链模式。
  3. 过滤器链

    • 当需要实现过滤器链时,可以使用责任链模式。

例子

  • 场景:你需要实现一个请假审批流程,请假请求可能由组长、经理、总监等不同级别人员审批。
  • 解决方案:使用责任链模式,将组长、经理、总监等审批者连成一条链,请求在链上传递,直到被处理。
  • 应用:审批流程、异常处理、日志记录等。

总结

  • 责任链模式:用于解耦请求的发送者和接收者,适用于多级审批、事件处理等场景。

策略模式(Strategy Pattern)

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互换。策略模式的核心思想是将算法的使用与算法的实现分离,使得算法可以独立于客户端变化。


组成成分

策略模式通常包含以下角色:

  1. 策略接口(Strategy)

    • 定义算法的接口。
  2. 具体策略(Concrete Strategy)

    • 实现策略接口,提供具体的算法实现。
  3. 上下文(Context)

    • 持有一个策略对象的引用,并提供设置策略和执行策略的方法。

特点

优点

  1. 解耦:将算法的使用与算法的实现分离,使得算法可以独立变化。
  2. 可扩展性:可以很容易地增加新的策略。
  3. 避免条件语句:通过策略模式可以避免使用复杂的条件语句。

缺点

  1. 策略类增多:如果策略较多,可能会导致策略类的数量增加。
  2. 客户端需要了解策略:客户端需要了解不同的策略,以便选择合适的策略。

使用场景

  1. 多种算法

    • 当有多种算法可供选择时,可以使用策略模式。
  2. 动态切换算法

    • 当需要在运行时动态切换算法时,可以使用策略模式。
  3. 避免条件语句

    • 当需要避免使用复杂的条件语句时,可以使用策略模式。

例子

  • 场景:你正在开发一个电商系统,支持不同的促销策略(如满减、折扣、赠品)。
  • 解决方案:使用策略模式,将每种促销策略封装为一个类,订单结算时根据条件选择合适的策略。
  • 应用:促销策略、排序算法、支付方式等。

总结

  • 策略模式:用于将算法的使用与实现分离,适用于多种算法、动态切换算法等场景。

模板方法模式(Template Method Pattern)

模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并将一些步骤延迟到子类中实现。模板方法模式的核心思想是将算法的结构固定,但允许某些步骤由子类实现


组成成分

模板方法模式通常包含以下角色:

  1. 抽象类(Abstract Class)

    • 定义算法的骨架,并包含一些抽象方法,由子类实现。
  2. 具体类(Concrete Class)

    • 实现抽象类中的抽象方法,为算法的某些步骤提供具体实现
  3. 模板方法(TemplateMethod)

    • 实现一系列步骤方法,完成算法的具体步骤

特点

优点

  1. 代码复用:将算法的公共部分放在抽象类中,避免代码重复。
  2. 扩展性:可以通过子类扩展算法的某些步骤。
  3. 控制结构:抽象类可以控制算法的结构,确保算法的正确执行。

缺点

  1. 复杂度增加:引入了更多的类和继承关系,增加了系统的复杂度。
  2. 灵活性受限:模板方法模式的结构固定,可能不适合需要灵活变化的场景。

使用场景

  1. 固定算法结构

    • 当算法的结构固定,但某些步骤需要由子类实现时,可以使用模板方法模式。
  2. 代码复用

    • 当多个子类有共同的算法结构时,可以使用模板方法模式。
  3. 控制算法流程

    • 当需要控制算法的执行流程时,可以使用模板方法模式。

例子

  • 场景:你需要实现冲泡不同类型的茶和咖啡,它们都有烧水、冲泡、倒入杯子等步骤,但冲泡材料不同。
  • 解决方案:使用模板方法模式,将冲泡流程定义为模板方法,子类实现具体的冲泡步骤。
  • 应用:算法框架、流程控制、代码复用等。

总结

  • 模板方法模式:用于固定算法的结构,适用于代码复用、控制算法流程等场景。

状态模式(State Pattern)

状态模式是一种行为型设计模式,它允许一个对象在其内部状态改变时改变其行为。状态模式的核心思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态对象。


组成成分

状态模式通常包含以下角色:

  1. 上下文(Context)

    • 维护一个当前状态对象的引用,并将与状态相关的行为委托给当前状态对象。
  2. 状态接口(State)

    • 定义状态的接口,通常包含与状态相关的方法。
  3. 具体状态(Concrete State)

    • 实现状态接口,提供与状态相关的具体行为。

特点

优点

  1. 解耦:将对象的状态与行为解耦,使得状态可以独立变化。
  2. 可扩展性:可以很容易地增加新的状态。
  3. 避免条件语句:通过状态模式可以避免使用复杂的条件语句。

缺点

  1. 复杂度增加:引入了更多的类和对象,增加了系统的复杂度。
  2. 状态转换逻辑:状态转换逻辑可能会变得复杂,尤其是在状态较多时。

使用场景

  1. 状态驱动行为

    • 当对象的行为依赖于其状态,并且状态转换频繁时,可以使用状态模式。
  2. 避免条件语句

    • 当需要避免使用复杂的条件语句来控制对象的行为时,可以使用状态模式。
  3. 状态机

    • 当需要实现状态机时,可以使用状态模式。

例子

  • 场景:你正在开发一个游戏,角色在不同状态(如正常、中毒、隐身)下有不同行为。
  • 解决方案:使用状态模式,将每个状态封装为一个类,角色根据当前状态执行不同行为。
  • 应用:游戏角色状态、订单状态、工作流状态等。

总结

  • 状态模式通过将对象的状态封装成独立的类,使得对象的行为可以随着状态的改变而改变。
  • 状态模式适用于状态驱动行为、避免条件语句和实现状态机等场景。
  • 状态模式能够有效地解耦对象的状态与行为,并提高系统的可扩展性。

迭代器模式(Iterator Pattern)

迭代器模式是一种行为型设计模式,它提供一种方法*顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。迭代器模式的核心思想是将遍历逻辑聚合对象分离,使得聚合对象可以专注于存储数据*,而迭代器专注于遍历数据

组成成分

迭代器模式通常包含以下角色:

  1. 迭代器接口(Iterator)

    • 定义遍历聚合对象的方法,如 Next()HasNext() 等。
  2. 具体迭代器(Concrete Iterator)

    • 实现迭代器接口,负责管理当前遍历位置
  3. 聚合接口(Aggregate)

    • 定义创建迭代器的方法。
  4. 具体聚合(Concrete Aggregate)

    • 实现聚合接口,返回一个具体迭代器

特点

优点

  1. 简化聚合对象:将遍历逻辑从聚合对象中分离出来,简化聚合对象的设计。
  2. 支持多种遍历方式:可以为同一个聚合对象提供多种遍历方式
  3. 解耦:客户端与聚合对象的内部表示解耦,客户端只需要依赖迭代器接口。

缺点

  1. 复杂度增加:引入了额外的迭代器类,增加了系统的复杂度。
  2. 性能开销:迭代器模式可能会引入额外的性能开销。

使用场景

  1. 遍历复杂数据结构

    • 当需要遍历复杂的数据结构(如树、图)时,可以使用迭代器模式。
  2. 统一遍历接口

    • 当需要为不同的聚合对象提供统一的遍历接口时,可以使用迭代器模式。
  3. 隐藏内部实现

    • 当需要隐藏聚合对象的内部实现时,可以使用迭代器模式。

例子

  • 场景:你需要遍历一个集合(如列表、数组)中的元素,但不希望暴露集合的内部结构。
  • 解决方案:使用迭代器模式,提供一个迭代器接口,隐藏集合的内部实现。
  • 应用:集合遍历、数据库查询结果遍历等。

总结

  • 迭代器模式:用于遍历聚合对象中的元素,适用于遍历复杂数据结构、统一遍历接口等场景,解决了对象之间的遍历问题。

非常用动作型模式

备忘录模式(Memento Pattern)

备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在以后可以将该对象恢复到原先保存的状态。备忘录模式的核心思想是将对象的状态保存到外部,并在需要时恢复

组成成分

备忘录模式通常包含以下角色:

  1. 发起人(Originator)

    • 负责创建一个备忘录,用于记录当前对象的内部状态,并可以使用备忘录恢复内部状态。
  2. 备忘录(Memento)

    • 存储发起人对象的内部状态。
  3. 管理者(Caretaker)

    • 负责保存备忘录,但不能对备忘录的内容进行操作或检查。

特点

优点

  1. 封装性:不破坏对象的封装性,可以保存和恢复对象的内部状态。
  2. 简化发起人:发起人不需要管理状态的保存和恢复逻辑。

缺点

  1. 资源消耗:如果状态数据较大,可能会消耗较多的内存资源。
  2. 复杂度增加:引入了额外的备忘录和管理者类,增加了系统的复杂度。

使用场景

  1. 撤销操作

    • 当需要实现撤销操作时,可以使用备忘录模式。
  2. 状态保存

    • 当需要保存对象的某个状态,以便在以后恢复时,可以使用备忘录模式。
  3. 快照功能

    • 当需要实现快照功能时,可以使用备忘录模式。

例子

  • 场景:你正在开发一个文本编辑器,需要支持撤销操作。
  • 解决方案:使用备忘录模式,保存文本编辑器的状态(如内容、光标位置),以便撤销时恢复。
  • 应用:撤销操作、游戏存档、事务回滚等。

命令模式(Command Pattern)

命令模式是一种行为型设计模式,它将请求封装为对象,从而使你可以用不同的请求对客户进行参数化。命令模式的核心思想是将请求的发送者与接收者解耦,使得请求的发送者不需要知道具体的接收者。


组成成分

命令模式通常包含以下角色:

  1. 命令接口(Command)

    • 定义执行操作的接口。
  2. 具体命令(Concrete Command)

    • 实现命令接口,封装具体的操作。
  3. 接收者(Receiver)

    • 知道如何执行与请求相关的操作。
  4. 调用者(Invoker)

    • 持有命令对象,并在需要时调用命令对象的执行方法。
  5. 客户端(Client)

    • 创建命令对象并设置其接收者。

特点

优点

  1. 解耦:将请求的发送者与接收者解耦,使得请求的发送者不需要知道具体的接收者。
  2. 可扩展性:可以很容易地增加新的命令。
  3. 支持撤销操作:可以很容易地实现撤销操作。

缺点

  1. 复杂度增加:引入了更多的类和对象,增加了系统的复杂度。

使用场景

  1. 菜单系统

    • 当需要实现菜单系统时,可以使用命令模式。
  2. 撤销操作

    • 当需要实现撤销操作时,可以使用命令模式。
  3. 任务队列

    • 当需要实现任务队列时,可以使用命令模式。

例子

  • 场景:你正在开发一个遥控器,每个按键对应一个命令(如开灯、关灯)。
  • 解决方案:使用命令模式,将每个命令封装为一个对象,遥控器通过执行命令对象来操作设备
  • 应用:菜单操作、任务调度、事务管理等。

中介者模式(Mediator Pattern)

中介者模式是一种行为型设计模式,它定义了一个中介对象来封装一系列对象之间的交互。中介者模式的核心思想是将对象之间的交互集中到中介者对象中,从而减少对象之间的直接耦合。


组成成分

中介者模式通常包含以下角色:

  1. 中介者接口(Mediator)

    • 定义与同事对象交互的接口。
  2. 具体中介者(Concrete Mediator)

    • 实现中介者接口,协调各同事对象之间的交互。
  3. 同事类(Colleague)

    • 定义同事类的接口,通常包含一个指向中介者的引用。
  4. 具体同事类(Concrete Colleague)

    • 实现同事类接口,并通过中介者与其他同事对象交互。

特点

优点

  1. 解耦:将对象之间的交互集中到中介者对象中,减少对象之间的直接耦合。
  2. 简化对象交互:简化了对象之间的交互逻辑。

缺点

  1. 中介者复杂性:中介者对象可能会变得复杂,尤其是在对象较多时。

使用场景

  1. 复杂交互

    • 当对象之间的交互复杂且相互依赖时,可以使用中介者模式。
  2. 减少耦合

    • 当需要减少对象之间的直接耦合时,可以使用中介者模式。
  3. 集中控制

    • 当需要集中控制对象之间的交互时,可以使用中介者模式。

例子

  • 场景:你正在开发一个聊天室系统,多个用户之间需要相互发送消息。
  • 解决方案:使用中介者模式,聊天室作为中介者,用户只需与聊天室交互,无需直接与其他用户交互。
  • 应用:聊天室、机场调度系统、GUI 组件交互等。

访问者模式(Visitor Pattern)

访问者模式是一种行为型设计模式,它允许你将算法与对象结构分离。访问者模式的核心思想是将算法从对象结构中分离出来,使得可以在不修改对象结构的情况下定义新的操作。


组成成分

访问者模式通常包含以下角色:

  1. 访问者接口(Visitor)

    • 定义访问对象结构的接口,通常包含多个访问方法。
  2. 具体访问者(Concrete Visitor)

    • 实现访问者接口,提供具体的访问操作。
  3. 元素接口(Element)

    • 定义接受访问者的接口。
  4. 具体元素(Concrete Element)

    • 实现元素接口,提供接受访问者的方法。
  5. 对象结构(Object Structure)

    • 包含元素的集合,并提供遍历元素的方法。

特点

优点

  1. 解耦:将算法与对象结构分离,使得算法可以独立变化。
  2. 可扩展性:可以很容易地增加新的访问操作。

缺点

  1. 复杂度增加:引入了更多的类和对象,增加了系统的复杂度。
  2. 破坏封装性:访问者模式可能会破坏对象的封装性。

使用场景

  1. 复杂对象结构

    • 当对象结构复杂,且需要对其执行多种操作时,可以使用访问者模式。
  2. 算法与对象结构分离

    • 当需要将算法与对象结构分离时,可以使用访问者模式。
  3. 动态添加操作

    • 当需要动态添加新的操作时,可以使用访问者模式。

例子

  • 场景:你正在开发一个编译器,需要对语法树节点(如变量声明节点、表达式节点)进行语义检查、代码生成等操作。
  • 解决方案:使用访问者模式,将每个操作封装为一个访问者类,语法树节点接受访问者并执行操作。
  • 应用:编译器、文档处理、规则引擎等。

解释器模式(Interpreter Pattern)

解释器模式是一种行为型设计模式,它定义了一个语言的文法,并使用解释器来解释语言中的句子。解释器模式的核心思想是将语言中的句子表示为一个抽象语法树,并通过解释器来解释执行。


组成成分

解释器模式通常包含以下角色:

  1. 抽象表达式(Abstract Expression)

    • 定义解释器的接口。
  2. 终结符表达式(Terminal Expression)

    • 实现抽象表达式接口,表示语言中的终结符。
  3. 非终结符表达式(Nonterminal Expression)

    • 实现抽象表达式接口,表示语言中的非终结符。
  4. 上下文(Context)

    • 包含解释器需要的信息。
  5. 客户端(Client)

    • 构建抽象语法树,并调用解释器解释执行。

特点

优点

  1. 可扩展性:可以很容易地扩展语言的文法。
  2. 易于实现:对于简单的文法,解释器模式很容易实现。

缺点

  1. 复杂度增加:对于复杂的文法,解释器模式可能会导致类的数量增加。
  2. 性能问题:解释器模式可能会导致性能问题,尤其是在解释复杂的句子时。

使用场景

  1. 简单语言

    • 当需要解释简单的语言时,可以使用解释器模式。
  2. 文法固定

    • 当语言的文法固定且不经常变化时,可以使用解释器模式。
  3. 动态脚本

    • 当需要实现动态脚本时,可以使用解释器模式。

例子

  • 场景:你需要实现一个简单的规则引擎,根据特定规则判断某些条件是否满足。
  • 解决方案:使用解释器模式,将规则表示为语法树,解释器遍历语法树并执行规则
  • 应用:规则引擎、小型 DSL(领域特定语言)、数学表达式解析等。

总结

  • 备忘录模式:用于保存和恢复对象的内部状态,适用于撤销操作、状态保存等场景。
  • 命令模式:用于将请求封装为对象,适用于菜单系统、撤销操作等场景。
  • 中介者模式:用于封装对象之间的交互,适用于复杂交互、减少耦合等场景。
  • 访问者模式:用于将算法与对象结构分离,适用于复杂对象结构、动态添加操作等场景。
  • 解释器模式:用于解释语言的句子,适用于简单语言、动态脚本等场景。

这些模式都是行为型设计模式,分别解决了状态保存、请求封装、对象交互、算法分离和语言解释等问题,理解它们的核心思想和应用场景有助于在实际开发中更好地应用设计模式。