设计模式学习之——策略模式

news/2024/12/2 22:29:06/

策略模式(Strategy Pattern)是一种行为型设计模式,它允许定义一系列算法,并将每个算法封装在独立的类中,使它们可以互相替换。策略模式通过将算法的使用与算法的实现分离,使得算法可以独立于客户端而变化。

一、策略模式的核心思想

策略模式的核心思想是面向接口编程,而不是面向实现编程。它通过将算法封装成独立的类,使得可以在运行时动态地选择和切换算法,从而提高代码的可维护性、扩展性和复用性。

二、策略模式的主要角色

策略模式结构图中,通常包含以下几个角色:

  1. 策略接口(Strategy):定义了一个或多个策略算法的方法,这些方法是所有具体策略类必须实现的。策略接口使得算法可以独立于使用它的客户而变化。
  2. 具体策略类(Concrete Strategies):实现了策略接口,提供了具体的算法实现。每个具体策略类都对应一种具体的算法。
  3. 上下文类(Context):持有一个策略接口的引用,在客户端调用上下文类的某个方法时,上下文类会把请求委托给当前持有的策略对象。上下文类通常还包含一个设置策略对象的方法,以便在运行时动态地切换策略。

三、策略模式的使用场景

策略模式通常适用于以下场景:

  1. 多算法选择:当需要在运行时根据情况选择不同的算法时,可以使用策略模式。例如,对于排序算法,根据数据量的不同可能选择快速排序、冒泡排序或插入排序等。
  2. 消除条件分支:当代码中存在大量的条件分支语句,并且这些条件分支都是根据相同的输入来选择不同的行为时,可以考虑使用策略模式来消除这些条件分支。这可以提高代码的可读性和可维护性。
  3. 算法的封装和复用:当系统中存在多个类似的算法,但它们的实现细节不同时,可以将这些算法封装成独立的策略类,以便复用和维护。
  4. 可扩展性:当需要为系统提供一种灵活、可拓展的方式来添加新的算法或行为时,策略模式可以帮助实现这一点,而无需修改现有的代码。
  5. 单一职责原则:当需要遵循单一职责原则,即每个类应该只负责一种功能时,策略模式可以将不同的算法分离到单独的策略类中,使得每个类都专注于一种算法。

四、策略模式的优缺点

优点:

  1. 提高了代码的灵活性和可维护性:通过策略模式,可以在运行时动态地选择和切换算法,而不需要修改客户端代码。这使得代码更加灵活和易于维护。
  2. 简化了单元测试:由于策略模式将算法封装在独立的类中,因此可以针对每个算法进行单独的单元测试,从而简化了测试过程。
  3. 遵循了开闭原则策略模式可以在不修改现有代码的情况下添加新的算法或行为,这符合开闭原则的要求。

缺点:

  1. 策略类数量多:当算法的数量较多时,会导致策略类的数量增加,从而增加了系统的复杂性。
  2. 客户端需要了解策略:客户端代码需要知道有哪些策略可供选择,并创建相应的策略对象,这可能会增加客户端的复杂度。

五、策略模式的示例

以下是几个简单的策略模式示例。

示例一:促销活动

  • 满减促销:当顾客购买一定金额的商品后,可以直接减去相应的金额。
  • 返现促销:在顾客购买商品后,返回一定比例的现金或优惠券。
  • 打折促销:对选定商品或全部商品进行打折处理。
  • 买赠促销:购买特定商品时赠送其他商品或服务
// 促销策略接口
public interface PromotionStrategy {double calculatePrice(double originalPrice);
}// 满减策略
public class FullReductionStrategy implements PromotionStrategy {private double threshold;private double reduction;public FullReductionStrategy(double threshold, double reduction) {this.threshold = threshold;this.reduction = reduction;}@Overridepublic double calculatePrice(double originalPrice) {if (originalPrice >= threshold) {return originalPrice - reduction;}return originalPrice;}
}// 打折策略
public class DiscountStrategy implements PromotionStrategy {private double discountRate;public DiscountStrategy(double discountRate) {this.discountRate = discountRate;}@Overridepublic double calculatePrice(double originalPrice) {return originalPrice * discountRate;}
}// 上下文类
public class ShoppingCart {private PromotionStrategy promotionStrategy;public void setPromotionStrategy(PromotionStrategy promotionStrategy) {this.promotionStrategy = promotionStrategy;}public double getTotalPrice(double originalPrice) {return promotionStrategy.calculatePrice(originalPrice);}
}// 客户端代码
public class PromotionDemo {public static void main(String[] args) {ShoppingCart cart = new ShoppingCart();// 使用满减策略cart.setPromotionStrategy(new FullReductionStrategy(100, 20));System.out.println("满减后价格: " + cart.getTotalPrice(120)); // 输出: 100.0// 使用打折策略cart.setPromotionStrategy(new DiscountStrategy(0.8));System.out.println("打折后价格: " + cart.getTotalPrice(120)); // 输出: 96.0}
}

示例二:排序与搜索 

  • 数组排序:根据不同需求,可以选择快速排序、归并排序、堆排序等算法进行数组排序。
  • 数据搜索:在大量数据中搜索目标值时,可以采用二分搜索、线性搜索等不同算法。
// 排序策略接口
public interface SortStrategy {void sort(int[] array);
}// 快速排序策略
public class QuickSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {// 快速排序的具体实现// ...// 这里为了简化,省略了具体实现System.out.println("执行快速排序");}
}// 冒泡排序策略
public class BubbleSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {// 冒泡排序的具体实现// ...// 这里为了简化,省略了具体实现System.out.println("执行冒泡排序");}
}// 上下文类
public class Sorter {private SortStrategy sortStrategy;public void setSortStrategy(SortStrategy sortStrategy) {this.sortStrategy = sortStrategy;}public void sortArray(int[] array) {sortStrategy.sort(array);}
}// 客户端代码
public class SortDemo {public static void main(String[] args) {int[] array = {5, 3, 8, 4, 2};Sorter sorter = new Sorter();// 使用快速排序sorter.setSortStrategy(new QuickSortStrategy());sorter.sortArray(array);// 使用冒泡排序sorter.setSortStrategy(new BubbleSortStrategy());sorter.sortArray(array);}
}

示例三:支付方式

  • 信用卡支付:使用信用卡结算,支持各种主流信用卡。
  • 第三方支付:如支付宝、微信支付等,满足不同用户习惯。
  • 货到付款:在收货时付款,提高用户信任度。
// 策略接口
public interface PaymentStrategy {void pay(int amount);
}// 具体策略类:信用卡支付
public class CreditCardPayment implements PaymentStrategy {private String cardNumber;public CreditCardPayment(String cardNumber) {this.cardNumber = cardNumber;}public void pay(int amount) {System.out.println(amount + " paid with credit card.");}
}// 具体策略类:PayPal支付
public class PayPalPayment implements PaymentStrategy {private String emailId;public PayPalPayment(String email) {this.emailId = email;}public void pay(int amount) {System.out.println(amount + " paid using PayPal.");}
}// 上下文类
public class ShoppingCart {private PaymentStrategy paymentStrategy;public void setPaymentStrategy(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}public void checkout(int amount) {paymentStrategy.pay(amount);}
}// 客户端代码
public class StrategyPatternExample {public static void main(String[] args) {ShoppingCart cart = new ShoppingCart();cart.setPaymentStrategy(new CreditCardPayment("1234567890123456"));cart.checkout(100);cart.setPaymentStrategy(new PayPalPayment("myemail@example.com"));cart.checkout(200);}
}

示例阶段小总结:(后面的示例将不再编写具体代码,参考方法套用实现即可)

定义策略接口:

首先,你需要定义一个策略接口,这个接口将声明所有支持的策略算法所共有的方法。这个接口就是算法的家族,为一系列具体的策略类提供了统一的接口

// 策略接口
public interface Strategy {void execute(); // 声明算法的方法
}

实现具体策略类:

然后,你需要为每一个具体的算法实现一个具体的策略类,这些类都实现了策略接口。每个具体策略类都提供了一个具体的算法实现。

// 具体策略类A
public class ConcreteStrategyA implements Strategy {public void execute() {// 算法A的具体实现System.out.println("执行算法A");}
}// 具体策略类B
public class ConcreteStrategyB implements Strategy {public void execute() {// 算法B的具体实现System.out.println("执行算法B");}
}

创建上下文类:

上下文类持有一个策略接口的引用,它维护一个对具体策略的引用。客户端代码通过调用上下文类的方法来间接调用具体策略的方法。上下文类还可以提供一个方法来设置当前使用的策略。

// 上下文类
public class Context {private Strategy strategy;// 设置当前策略public void setStrategy(Strategy strategy) {this.strategy = strategy;}// 执行当前策略的方法public void executeStrategy() {strategy.execute();}
}

客户端代码:

最后,在客户端代码中,你可以根据需要动态地设置和切换策略。客户端代码通过创建上下文对象,并设置具体的策略来实现这一点。

// 客户端代码
public class StrategyPatternDemo {public static void main(String[] args) {Context context = new Context();// 使用算法Acontext.setStrategy(new ConcreteStrategyA());context.executeStrategy();// 使用算法Bcontext.setStrategy(new ConcreteStrategyB());context.executeStrategy();}
}
关键点总结
  • 策略接口:定义了所有策略算法所共有的方法。
  • 具体策略类:实现了策略接口,提供了具体的算法实现。
  • 上下文类:持有一个策略接口的引用,通过调用该接口的方法来间接调用具体策略的方法。
  • 客户端代码:通过创建上下文对象,并设置具体的策略来实现算法的动态选择和切换。

示例四:数据压缩

  • 无损压缩:如zip、gzip等,保证数据完整性。
  • 有损压缩:如jpeg、mp3等,适用于图片和音频数据。

示例五:游戏开发

  • 敌人行为:根据不同关卡和难度,切换敌人的行为策略。
  • 玩家辅助:根据玩家等级提供不同的辅助策略,如自动瞄准、提示等。

示例六:出行选择

  • 出行方式的选择,如乘坐飞机、火车、自行车等,可以根据天气、距离、时间紧迫等因素决定采用哪一种方式出行。

示例七:表单验证

  • 一个表单验证工具可以根据不同的验证规则采用不同的验证策略,例如长度验证、格式验证等。

示例八:个人所得税计算

  • 不同国家或地区的个人所得税计算方法可能不同,策略模式可以使得这些不同的计算方法可以相互替换。

示例九:动物叫声模拟

  • 可以模拟不同动物的叫声,如狗的汪汪叫、猫的喵喵叫、鸟的啾啾叫等。

通过策略模式,可以将算法或行为与具体的业务逻辑解耦,使得系统更加灵活和可扩展。它允许在运行时动态地选择和切换算法,而无需修改原有的代码,从而提高了代码的复用性和可维护性。同时,策略模式也符合开闭原则,即对扩展开放,对修改关闭,使得系统可以更容易地应对未来可能的需求变化。


http://www.ppmy.cn/news/1551861.html

相关文章

Webman中实现定时任务

文章目录 Webman中实现定时任务一、引言二、安装与配置1、安装Crontab组件2、创建进程文件3、配置进程文件随Webman启动4、重启Webman5、Cron表达式(补充)例子 三、使用示例四、总结 Webman中实现定时任务 一、引言 在现代的后端开发中,定时…

栈和队列——考研笔记

文章目录 一.栈(Stack)基本概念1.栈的基本操作2.栈的常考题型 二.顺序栈的实现1.顺序栈的定义2.增(进栈操作)3.删(出栈操作)4.共享栈(两个栈共享同一片空间) 三.链栈的实现1.头插法建…

青少年编程等级一级 自动打包机问题

一条哈密瓜自动打包流水线的工作程序是这样的:首先系统设定每箱哈密瓜应该有的总重量 W;然后传送带将一只只哈密瓜输送到一个自动称重设备上,根据称重结果进行以下操作:- 如果称上的总重量正好达到 W,则将称上的所有哈…

【CSS in Depth 2 精译_063】10.2 深入理解 CSS 容器查询中的容器

当前内容所在位置(可进入专栏查看其他译好的章节内容) 【第十章 CSS 容器查询】 ✔️ 10.1 容器查询的一个简单示例 10.1.1 容器尺寸查询的用法 10.2 深入理解容器 ✔️ 10.2.1 容器的类型 ✔️10.2.2 容器的名称 ✔️10.2.3 容器与模块化 CSS ✔️ 10.3…

数据库学习记录02

DQL【数据查询语言】 1.基础查询 1.1语法 select * | {[DISTINCT] column | expression[alias], ...} from table; 特点 查询列表可以是表中的字段、常量值、表达式、函数。 查询的结果是一个虚拟的表格。 #1.查询表中的单个字段 select name from employees;#2.查询表中…

网络安全之IP伪造

眼下非常多站点的涉及存在一些安全漏洞,黑客easy使用ip伪造、session劫持、xss攻击、session注入等手段危害站点安全。在纪录片《互联网之子》(建议搞IT的都要看下)中。亚伦斯沃茨(真实人物,神一般的存在)涉…

如何构建一个高效安全的图书管理系统

文章目录 技术栈功能需求实现步骤1. 准备开发环境2. 创建项目结构3. 配置数据库4. 创建实体类5. 创建仓库接口6. 创建服务类7. 创建控制器8. 创建前端页面9. 运行项目 技术栈 前端:HTML5、CSS3、JavaScript后端:Java(Spring Boot框架&#x…

IT人日常健康工作生活方案

1. 早餐(7:00-8:00) 早餐是一天中最重要的一餐,提供充足的能量来启动新的一天。根据亚洲饮食的特点,我们加入了米饭、豆腐、蔬菜等传统食材,同时保持高蛋白、低糖的原则。 糙米粥或小米粥(1碗):低GI碳水化合物,有助于稳定血糖,提供持久能量。可加入少量的红枣、枸杞…