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

设计模式使用场景实现示例及优缺点(行为型模式——策略模式)

策略模式(Strategy Pattern)

策略模式(Strategy Pattern)是一种行为设计模式,它能够在运行时选择最适合的算法或行为,同时能够将算法族封装成独立的类,并使它们之间可以相互替换。这种模式是通过创建一个共同的接口,而后将不同的行为或算法封装在不同的策略类中实现的。每个策略类都遵循相同的接口,从而保持策略的独立性与互换性。

核心组件

  • Strategy(策略接口):这是一个共同的接口,它定义了所有支持的算法的抽象方法。任何具体策略都必须实现这个接口。
  • ConcreteStrategy(具体策略):实现策略接口的类,提供具体的算法实现。
  • Context(上下文):用来维护对策略对象的引用,它可以定义一个接口,让策略对象根据上下文来选择适当的算法。

适用场景

  1. 多种算法或行为
    • 当一个类存在多种行为,且使用条件时,可以将这些行为封装成不同的策略。
  2. 避免使用多重条件选择语句
    • 使用策略模式可以避免使用多重条件选择语句,这样可以更容易维护和扩展。
  3. 需要动态地改变算法或行为
    • 当算法或行为需要经常改变时,使用策略模式可以提供更好的代码组织和重新使用。
  4. 行为变化独立于使用行为的客户
    • 需要将行为与客户代码解耦,使得行为的改变不会影响客户代码。

实现实例

以电商系统的支付功能为例,假设需要支持多种支付方式(如信用卡、PayPal、比特币等)。使用策略模式可以定义一个支付接口(PaymentStrategy),并为每种支付方式实现一个具体的策略类。上下文(PaymentContext)可以持有一个支付策略引用,根据不同的用户选择使用不同的支付策略:

策略接口(Strategy Interface)

这个接口定义了所有支持的算法或行为的抽象方法。每个具体的策略类都必须实现这个接口。

public interface PaymentStrategy {void pay(int amount);  // 定义支付行为的方法,每种支付策略都需要实现这个方法
}
具体策略类(Concrete Strategy Classes)

这些类实现了策略接口,并提供了具体的算法实现。

public class CreditCardStrategy implements PaymentStrategy {public void pay(int amount) {System.out.println("Paid " + amount + " using Credit Card");  // 信用卡支付实现}
}public class PayPalStrategy implements PaymentStrategy {public void pay(int amount) {System.out.println("Paid " + amount + " using PayPal");  // PayPal支付实现}
}public class BitcoinStrategy implements PaymentStrategy {public void pay(int amount) {System.out.println("Paid " + amount + " using Bitcoin");  // 比特币支付实现}
}
上下文类(Context Class)

这个类用于维护对策略对象的引用。它可以定义一个方法让策略对象根据上下文来选择适当的算法。

public class PaymentContext {private PaymentStrategy strategy;  // 维护一个对策略对象的引用public PaymentContext(PaymentStrategy strategy) {this.strategy = strategy;  // 构造函数中设置策略对象}public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;  // 允许在运行时改变策略}public void executePayment(int amount) {strategy.pay(amount);  // 执行支付,具体行为取决于策略对象}
}
客户端代码(Client Code)

这部分代码演示了如何使用策略模式来改变对象的行为。

public class Client {public static void main(String[] args) {PaymentContext context = new PaymentContext(new CreditCardStrategy());context.executePayment(100);  // 使用信用卡策略支付100context.setStrategy(new PayPalStrategy());context.executePayment(200);  // 更改策略为PayPal并支付200context.setStrategy(new BitcoinStrategy());context.executePayment(300);  // 更改策略为比特币并支付300}
}

优缺点

优点
  1. 封装性好
    • 策略模式将每个变化的策略封装到独立的类中,使得每个策略可以独立于客户端实现变化。
  2. 易于扩展
    • 策略模式提供了一种扩展机制,新的策略类可以很容易地添加进现有系统中。
  3. 避免使用多重条件选择语句
    • 策略模式允许动态地改变行为,客户端仅需更改配置,无需修改代码。
缺点
  1. 客户端必须知道所有策略
    • 客户端需要了解所有的策略类,并自行决定使用哪一个策略类。
  2. 策略族的增多
    • 随着策略族的增加,各种策略类的数目也会增加,每个策略都需要对外暴露,这就增加了系统的复杂性。

类图

+----------------+         +------------------+
|     Context    |-------->|     Strategy     |
+----------------+         +------------------+
| - strategy:    |         | + execute()      |
|   Strategy     |         +------------------+
| + setStrategy()|                 ^
| + execute()    |                 |
+----------------+                 ||+-------------------+--------+--------+----------------+|                   |                 |                |
+---------------+ +-----------------+ +----------------+ +--------------+
|ConcreteStrategyA| |ConcreteStrategyB| |ConcreteStrategyC| | ...          |
+---------------+ +-----------------+ +----------------+ +--------------+
| + execute()   | | + execute()     | | + execute()    | | + execute()   |
+---------------+ +-----------------+ +----------------+ +--------------+

总结

策略模式提供了一种灵活的方式来切换对象的行为,增强了代码的可维护性和扩展性。它帮助将行为封装为对象,可以在运行时互换,这使得它在需要支持多种行为的系统中非常有用。这种模式特别适合于那些算法或行为多样化的场景,可以有效地帮助系统遵守开闭原则,即对扩展开放,对修改关闭。通过策略模式,程序员可以方便地添加新的策略而不影响现有的系统,并且能够在运行时动态地改变对象的行为。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • leetcode 147. 对链表进行插入排序
  • Kafka基础入门-代码实操
  • 易懂的吉文斯(Givens)变换(一)
  • 如何使用Gunicorn配置SSL/TLS加密Web服务
  • 序列化与反序列化及不同序列化方式的性能对比
  • 第四章 Redis(2023版本IDEA)
  • SVN 分支管理深入解析
  • 机器人三定律及伦理分析
  • 通过 PPPOE 将 linux 服务器作为本地局域网 IPv4 外网网关
  • Zookeeper-数据结构
  • 优化Cocos Creator 包体体积
  • IDEA启动Web项目总是提示端口占用
  • VsCode远程ssh连接失败:Could not establish connection to XXX
  • Vue3学习体验(一)
  • Reinforced Causal Explainer for GNN论文笔记
  • 自己简单写的 事件订阅机制
  • 【Linux系统编程】快速查找errno错误码信息
  • Git的一些常用操作
  • leetcode386. Lexicographical Numbers
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 分享一份非常强势的Android面试题
  • 两列自适应布局方案整理
  • 每天10道Java面试题,跟我走,offer有!
  • 日剧·日综资源集合(建议收藏)
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 栈实现走出迷宫(C++)
  • ​马来语翻译中文去哪比较好?
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • (七)Appdesigner-初步入门及常用组件的使用方法说明
  • (转)Windows2003安全设置/维护
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .net MVC中使用angularJs刷新页面数据列表
  • .NET NPOI导出Excel详解
  • .NET 中 GetProcess 相关方法的性能
  • .NET的微型Web框架 Nancy
  • .NET未来路在何方?
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .NET中两种OCR方式对比
  • .sys文件乱码_python vscode输出乱码
  • .vimrc 配置项
  • @AliasFor 使用
  • @RequestBody与@RequestParam
  • [ solr入门 ] - 利用solrJ进行检索
  • [12] 使用 CUDA 进行图像处理
  • [2021]Zookeeper getAcl命令未授权访问漏洞概述与解决