当前位置: 首页 > news >正文

设计模式(五)

状态模式(State Pattern)详解

一、核心概念

状态模式允许对象在其内部状态改变时改变其行为,使得对象看起来如同修改了其类。该模式将状态相关的行为封装在独立的状态类中,并通过统一接口进行切换。
通过切换状态来实现切换行为。

当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了

核心组件

  1. 上下文(Context):持有当前状态的引用,并将状态相关行为委托给当前状态。
  2. 抽象状态(State):定义状态接口,封装与上下文特定状态相关的行为。
  3. 具体状态(Concrete State):实现抽象状态接口,处理特定状态下的行为,并可切换上下文的状态。
二、代码示例:电梯控制系统

场景:电梯有多种状态(开门、关门、运行、停止),不同状态下响应不同命令。

#include <iostream>
#include <string>// 前置声明
class Elevator;// 抽象状态类
class ElevatorState {
public:virtual ~ElevatorState() = default;virtual void openDoor(Elevator* elevator) = 0;virtual void closeDoor(Elevator* elevator) = 0;virtual void run(Elevator* elevator) = 0;virtual void stop(Elevator* elevator) = 0;virtual std::string getStateName() const = 0;
};// 上下文类:电梯
class Elevator {
private:ElevatorState* currentState;public:explicit Elevator(ElevatorState* state) : currentState(state) {}~Elevator() { delete currentState; }void setState(ElevatorState* state) {std::cout << "电梯状态从 " << currentState->getStateName() << " 变为 " << state->getStateName() << std::endl;delete currentState;currentState = state;}// 委托给当前状态处理void openDoor() { currentState->openDoor(this); }void closeDoor() { currentState->closeDoor(this); }void run() { currentState->run(this); }void stop() { currentState->stop(this); }
};// 具体状态:开门状态
class DoorOpenState : public ElevatorState {
public:void openDoor(Elevator* elevator) override {std::cout << "电梯门已开,无需重复操作" << std::endl;}void closeDoor(Elevator* elevator) override {std::cout << "电梯门正在关闭..." << std::endl;elevator->setState(new DoorClosedState());}void run(Elevator* elevator) override {std::cout << "电梯门未关,无法运行" << std::endl;}void stop(Elevator* elevator) override {std::cout << "电梯未运行,无需停止" << std::endl;}std::string getStateName() const override { return "开门状态"; }
};// 具体状态:关门状态
class DoorClosedState : public ElevatorState {
public:void openDoor(Elevator* elevator) override {std::cout << "电梯门正在打开..." << std::endl;elevator->setState(new DoorOpenState());}void closeDoor(Elevator* elevator) override {std::cout << "电梯门已关闭,无需重复操作" << std::endl;}void run(Elevator* elevator) override {std::cout << "电梯开始运行..." << std::endl;elevator->setState(new RunningState());}void stop(Elevator* elevator) override {std::cout << "电梯未运行,无需停止" << std::endl;}std::string getStateName() const override { return "关门状态"; }
};// 具体状态:运行状态
class RunningState : public ElevatorState {
public:void openDoor(Elevator* elevator) override {std::cout << "电梯运行中,不能开门" << std::endl;}void closeDoor(Elevator* elevator) override {std::cout << "电梯运行中,门已关闭" << std::endl;}void run(Elevator* elevator) override {std::cout << "电梯已在运行中" << std::endl;}void stop(Elevator* elevator) override {std::cout << "电梯正在停止..." << std::endl;elevator->setState(new StoppedState());}std::string getStateName() const override { return "运行状态"; }
};// 具体状态:停止状态
class StoppedState : public ElevatorState {
public:void openDoor(Elevator* elevator) override {std::cout << "电梯门正在打开..." << std::endl;elevator->setState(new DoorOpenState());}void closeDoor(Elevator* elevator) override {std::cout << "电梯门已关闭,无需重复操作" << std::endl;}void run(Elevator* elevator) override {std::cout << "电梯开始运行..." << std::endl;elevator->setState(new RunningState());}void stop(Elevator* elevator) override {std::cout << "电梯已停止,无需重复操作" << std::endl;}std::string getStateName() const override { return "停止状态"; }
};// 客户端代码
int main() {// 创建电梯,初始状态为开门Elevator elevator(new DoorOpenState());// 执行一系列操作elevator.closeDoor();  // 开门 -> 关门elevator.run();        // 关门 -> 运行elevator.openDoor();   // 运行中,无法开门elevator.stop();       // 运行 -> 停止elevator.openDoor();   // 停止 -> 开门return 0;
}
三、状态模式的优势
  1. 解耦状态与行为

    • 将特定状态的行为封装在独立类中,避免使用大量if-elseswitch-case
  2. 符合开闭原则

    • 新增状态只需添加新的具体状态类,无需修改现有代码。
  3. 状态转换显式化

    • 状态转换逻辑集中在状态类内部,便于维护和理解。
  4. 行为局部化

    • 特定状态的行为由该状态类完全负责,降低了上下文类的复杂度。
四、状态模式与策略模式的对比
维度状态模式(State)策略模式(Strategy)
核心意图封装基于状态的行为,状态间可自动转换封装可互换的算法,由客户端主动选择
状态管理状态由上下文或状态类自身管理,自动转换策略由客户端管理,需显式切换
依赖关系上下文依赖抽象状态,具体状态相互知晓上下文依赖抽象策略,具体策略相互独立
使用场景行为随状态变化而变化,状态转换逻辑复杂算法可互相替换,客户端需灵活选择
五、状态模式的适用场景
  1. 对象行为依赖状态

    • 如电商订单状态(待支付、已支付、已发货、已完成)。
  2. 状态转换频繁

    • 如游戏角色状态(站立、行走、跳跃、攻击)。
  3. 条件语句复杂

    • 当大量if-elseswitch-case使代码难以维护时。
六、状态模式的实现变种
  1. 共享状态实例

    • 对于无状态的具体状态类,可使用单例模式避免重复创建。
  2. 状态工厂

    • 使用工厂模式创建状态实例,集中管理状态创建逻辑。
  3. 状态机框架

    • 复杂场景下,可使用第三方状态机库(如Boost.Statechart)简化开发。
七、注意事项
  1. 内存管理

    • 状态类通常由上下文持有,需注意析构顺序避免内存泄漏。
  2. 状态爆炸

    • 若状态数量过多,可能导致类膨胀,需考虑状态分层或组合。
  3. 状态转换逻辑

    • 状态转换规则应集中管理,避免分散在多个状态类中。

状态模式是处理复杂状态逻辑的有效工具,通过将状态相关行为封装到独立类中,使代码更清晰、可维护和可扩展。

策略模式(Strategy Pattern)详解

一、核心概念

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。

核心组件

  1. 策略接口(Strategy):定义所有支持的算法的公共接口。
  2. 具体策略(Concrete Strategy):实现策略接口的具体算法。
  3. 上下文(Context):持有一个策略对象的引用,负责使用策略。
二、代码示例:支付系统

场景:电商系统支持多种支付方式(信用卡、支付宝、微信支付)。

#include <iostream>
#include <string>// 策略接口:支付方式
class PaymentStrategy {
public:virtual ~PaymentStrategy() = default;virtual void pay(double amount) const = 0;
};// 具体策略:信用卡支付
class CreditCardPayment : public PaymentStrategy {
private:std::string cardNumber;std::string cvv;std::string expiryDate;public:CreditCardPayment(const std::string& cardNum, const std::string& cvv, const std::string& expiry): cardNumber(cardNum), cvv(cvv), expiryDate(expiry) {}void pay(double amount) const override {std::cout << "使用信用卡支付: " << amount << " 元" << std::endl;std::cout << "卡号: " << cardNumber << ", CVV: " << cvv << ", 有效期: " << expiryDate << std::endl;}
};// 具体策略:支付宝支付
class AlipayPayment : public PaymentStrategy {
private:std::string userId;public:explicit AlipayPayment(const std::string& id) : userId(id) {}void pay(double amount) const override {std::cout << "使用支付宝支付: " << amount << " 元" << std::endl;std::cout << "支付宝账户: " << userId << std::endl;}
};// 具体策略:微信支付
class WechatPayment : public PaymentStrategy {
private:std::string openId;public:explicit WechatPayment(const std::string& id) : openId(id) {}void pay(double amount) const override {std::cout << "使用微信支付: " << amount << " 元" << std::endl;std::cout << "微信ID: " << openId << std::endl;}
};// 上下文:订单
class Order {
private:double amount;PaymentStrategy* paymentStrategy;public:explicit Order(double orderAmount) : amount(orderAmount), paymentStrategy(nullptr) {}~Order() { delete paymentStrategy; }void setPaymentStrategy(PaymentStrategy* strategy) {delete paymentStrategy;paymentStrategy = strategy;}void processPayment() const {if (!paymentStrategy) {std::cout << "请先选择支付方式" << std::endl;return;}paymentStrategy->pay(amount);}
};// 客户端代码
int main() {// 创建订单Order order(99.99);// 选择支付方式(策略)order.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456", "123", "12/25"));order.processPayment();// 切换支付方式order.setPaymentStrategy(new AlipayPayment("user@example.com"));order.processPayment();return 0;
}
三、策略模式的优势
  1. 算法可互换

    • 客户端可以在运行时动态切换算法,无需修改上下文。
  2. 解耦算法与客户端

    • 算法的实现和使用分离,符合单一职责原则。
  3. 扩展性强

    • 新增策略无需修改现有代码,符合开闭原则。
  4. 消除条件语句

    • 避免使用大量if-elseswitch-case来选择算法。
四、策略模式与状态模式的对比
维度策略模式(Strategy)状态模式(State)
核心意图封装可互换的算法,由客户端主动选择封装基于状态的行为,状态间可自动转换
状态管理策略由客户端管理,需显式切换状态由上下文或状态类自身管理,自动转换
依赖关系上下文依赖抽象策略,具体策略相互独立上下文依赖抽象状态,具体状态相互知晓
使用场景算法可互相替换,客户端需灵活选择行为随状态变化而变化,状态转换逻辑复杂
五、策略模式的适用场景
  1. 多种算法选择

    • 如排序算法(冒泡排序、快速排序、归并排序)。
  2. 动态切换行为

    • 如游戏中的角色移动方式(步行、跑步、飞行)。
  3. 避免条件语句

    • 当算法选择逻辑复杂时,使用策略模式替代条件语句。
六、策略模式的实现变种
  1. 策略工厂

    • 使用工厂模式创建策略实例,隐藏具体策略的创建逻辑。
  2. 参数化策略

    • 通过构造函数或设置方法向策略传递参数,增强灵活性。
  3. 策略注册表

    • 使用注册表存储策略,支持动态注册和发现策略。
七、注意事项
  1. 客户端需了解策略

    • 客户端必须知道所有策略的区别,才能选择合适的策略。
  2. 策略数量控制

    • 过多的策略类可能导致类爆炸,可考虑使用策略组合或分层。
  3. 策略共享

    • 无状态的策略可设计为单例,减少对象创建开销。

策略模式是处理算法变化的有效工具,通过将算法封装在独立的策略类中,使代码更具灵活性和可维护性。

适配器模式(Adapter Pattern)详解

一、核心概念

适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间,其核心思想是通过一个中间组件(适配器)来兼容不同的接口。
需要的东西就在面前,但却不能使用,而短时间又无法改造它,于是我们就想办法适配它。比如笔记本电脑是不能什么电压都能用的,但国家不同,电压可能不相同也是事实,于是就用一个电源适配器,只要是电,不管多少伏,都能把电源变成需要的电压,这就是电源适配器的作用。适配器的意思就是使得一个东西适合另一个东西的东西。
适配器也可以看作“ 翻译 ”,使不兼容的两个系统间能够彼此沟通。

什么时候需要适配器?
在软件开发中,也就是系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,比如在需要对早期代码复用一些功能等应用上很有实际价值。

核心角色

  1. 目标接口(Target):客户期望的接口,定义了特定领域的方法。
  2. 适配者(Adaptee):现有接口,需要被适配的类。
  3. 适配器(Adapter):实现目标接口并包装适配者,将请求转换为适配者的接口调用。
二、实现方式

适配器模式有两种主要实现方式:

  1. 类适配器:通过多重继承同时实现目标接口和适配者类。
  2. 对象适配器:通过组合持有适配者实例,更灵活且常用。
三、代码示例

场景:将第三方矩形绘制库适配为统一的形状接口。

1. 对象适配器实现
#include <iostream>
#include <string>// 目标接口:形状
class Shape {
public:virtual void draw() = 0;virtual ~Shape() = default;
};// 适配者:第三方矩形类(接口不兼容)
class ThirdPartyRectangle {
public:void drawRectangle(double x1, double y1, double x2, double y2) {std::cout << "第三方库绘制矩形:从 (" << x1 << "," << y1 << ") 到 (" << x2 << "," << y2 << ")" << std::endl;}
};// 对象适配器:将矩形适配为形状接口
class RectangleAdapter : public Shape {
private:ThirdPartyRectangle* adaptee;  // 组合适配者double x1, y1, x2, y2;          // 矩形坐标public:RectangleAdapter(double x1, double y1, double x2, double y2) : adaptee(new ThirdPartyRectangle()), x1(x1), y1(y1), x2(x2), y2(y2) {}~RectangleAdapter() override { delete adaptee; }// 实现目标接口方法void draw() override {adaptee->drawRectangle(x1, y1, x2, y2);  // 转换调用}
};// 客户端代码
int main() {// 使用适配器创建矩形(通过形状接口)Shape* rectangle = new RectangleAdapter(10, 10, 50, 50);rectangle->draw();  // 输出:第三方库绘制矩形:从 (10,10) 到 (50,50)delete rectangle;return 0;
}
2. 类适配器实现(C++多重继承)
// 目标接口(同上)
class Shape {
public:virtual void draw() = 0;virtual ~Shape() = default;
};// 适配者(同上)
class ThirdPartyRectangle {
public:void drawRectangle(double x1, double y1, double x2, double y2) {std::cout << "第三方库绘制矩形:从 (" << x1 << "," << y1 << ") 到 (" << x2 << "," << y2 << ")" << std::endl;}
};// 类适配器:通过多重继承同时实现目标接口和适配者
class RectangleAdapter : public Shape, private ThirdPartyRectangle {
private:double x1, y1, x2, y2;public:RectangleAdapter(double x1, double y1, double x2, double y2) : x1(x1), y1(y1), x2(x2), y2(y2) {}// 实现目标接口方法void draw() override {drawRectangle(x1, y1, x2, y2);  // 直接调用适配者方法}
};
四、双向适配器

允许两个不兼容的接口互相适配:

class Shape {
public:virtual void draw() = 0;
};class Rectangle {
public:virtual void drawRect(double x1, double y1, double x2, double y2) = 0;
};class DoubleAdapter : public Shape, public Rectangle {
private:Shape* shape;Rectangle* rect;public:DoubleAdapter(Shape* s) : shape(s), rect(nullptr) {}DoubleAdapter(Rectangle* r) : shape(nullptr), rect(r) {}// 实现Shape接口void draw() override {if (rect) rect->drawRect(0, 0, 100, 100);}// 实现Rectangle接口void drawRect(double x1, double y1, double x2, double y2) override {if (shape) shape->draw();}
};
五、适配器模式的优势
  1. 接口兼容性

    • 使不兼容的接口可以协同工作,提高代码复用性。
  2. 松耦合设计

    • 客户端与适配者解耦,只依赖目标接口。
  3. 开闭原则

    • 新增适配器无需修改现有代码,符合扩展性要求。
  4. 双向适配

    • 支持双向适配,使两个方向的接口调用都能兼容。
六、适用场景
  1. 整合第三方库

    • 当现有接口与第三方库不兼容时,使用适配器转换接口。
  2. 系统升级

    • 旧系统接口与新系统不兼容时,通过适配器过渡。
  3. 复用遗留代码

    • 将不兼容的遗留代码封装在适配器中,与新系统集成。
  4. 统一多个相似接口

    • 将多个不同但功能相似的类,通过适配器统一到一个接口下。
七、与其他模式的对比
  1. 与桥接模式的区别

    • 适配器是事后补救(解决已存在的不兼容),桥接是事前设计(分离抽象与实现)。
  2. 与装饰器模式的区别

    • 适配器改变接口,装饰器增强功能但不改变接口。
  3. 与外观模式的区别

    • 适配器转换单个接口,外观为子系统提供统一接口。
八、注意事项
  1. 避免过度使用

    • 适配器模式可能掩盖设计缺陷,应优先考虑重构接口而非适配。
  2. 性能开销

    • 对象适配器通过组合实现,可能增加一层调用开销。
  3. 双向适配器复杂性

    • 双向适配器增加了代码复杂度,需谨慎使用。

适配器模式是解决接口不兼容问题的有效工具,通过引入中间层(适配器),使原本无法协作的类能够无缝集成。在实际开发中,它常用于整合第三方库、系统升级过渡或复用遗留代码。

http://www.lryc.cn/news/577054.html

相关文章:

  • Java面试宝典:基础五
  • pyhton基础【18】面向对象基础一
  • LRU缓存设计与实现详解
  • XWPFDocument导出word文件
  • 使用component封装组件和h函数的用法
  • 71. 简化路径 —day94
  • Utils系列之内存池(Fixed size)
  • Elasticsearch 集群升级实战指引—7.x 升级到 8.x
  • 【C++】C++中的友元函数和友元类
  • Prompt Depth Anything:以提示方式驱动的Depth Anything用于实现4K分辨率下的精确米制深度估计
  • 大事件项目记录12-文章管理接口开发-总
  • 【学习】《算法图解》第八章学习笔记:平衡树
  • springboot校园新闻网站
  • 基于Pandas和FineBI的昆明职位数据分析与可视化实现(三)- 职位数据统计分析
  • 二叉树找到下一个中序遍历节点的思路
  • MATLAB仿真:经过大气湍流的涡旋光束的光斑漂移
  • 消息队列:Redis Stream到RabbitMQ的转换
  • python中*args, **kwargs到底是什么意思
  • Mac使用VMware安装win11使用Origin绘图巨卡解决办法
  • linux运维学习第10周
  • 智能新纪元:大语言模型如何重塑电商“人货场”经典范式
  • 条件概率:不确定性决策的基石
  • Oracle 递归 + Decode + 分组函数实现复杂树形统计进阶(第二课)
  • 中介者模式 - Flutter中的通信指挥中心,告别组件间混乱对话!
  • 怎样学习STM32
  • Springboot 集成 SpringBatch 批处理组件
  • 2.安装Docker
  • 力扣第87题-扰乱字符串
  • 如何通过自动化减少重复性工作
  • Vue中的v-if与emit事件传递:一个常见陷阱分析