策略模式是一个非常实用的设计模式,指定义了一类算法并将其封装起来,并使得它们之间可以灵活地切换,并且不影响客户端。

1,从一个例子开始

我们常常会在网上买东西,很多购物平台都会有着各种各样的优惠策略供你选择例如满减优惠、返现优惠等等。

假设现在要开发一个商城系统,并要开发优惠策略,需要实现不使用优惠满减优惠返现优惠三个策略。

这一步很简单,我们将优惠进行抽象,创建一个优惠接口Promotion如下:

package fun.swsk33site.strategy.promotion;import fun.swsk33site.strategy.model.Order;/*** 促销活动抽象接口*/
public interface Promotion {/*** 执行促销策略** @param order 传入订单进行相应折扣*/void doPromotion(Order order);}
复制代码

然后创建该接口的实现类NoPromotionFullDiscountPromotionCashBackPromotion分别代表不使用优惠、满减优惠和返现优惠:

package fun.swsk33site.strategy.promotion.impl;import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;/*** 不使用优惠*/
public class NoPromotion implements Promotion {@Overridepublic void doPromotion(Order order) {System.out.println("不使用任何优惠");}}
复制代码
package fun.swsk33site.strategy.promotion.impl;import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;/*** 满减促销*/
public class FullDiscountPromotion implements Promotion {@Overridepublic void doPromotion(Order order) {// 满200减20if (order.getPrice() > 200) {order.setPrice(order.getPrice() - 20);System.out.println("使用了满200减20优惠");}}}
复制代码
package fun.swsk33site.strategy.promotion.impl;import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;/*** 返现促销*/
public class CashBackPromotion implements Promotion {@Overridepublic void doPromotion(Order order) {// 满200返现20if (order.getPrice() > 200) {// 返现20...System.out.println("使用了满200返现20到支付宝账户优惠");}}}
复制代码

最后创建客户端类执行支付逻辑:

Order order1 = new Order();
order1.setName("xxx");
order1.setPrice(123);
String strategy = "fulldiscount";
Promotion promotion = null;
if (strategy.equals("no")) {promotion = new NoPromotion();
} else if (strategy.equals("fulldiscount")) {promotion = new FullDiscountPromotion();
} else if (strategy.equals("cashback")) {promotion = new CashBackPromotion();
}
promotion.doPromotion(order1);
复制代码

很显然这样写是完全不实用的,如果说促销活动越来越多,那么客户端的代码将会越来越复杂,越来越臃肿。

2,使用策略模式改造

我们可以单独创建一个类,这个类专门用于来根据传入参数选择不同的策略。

首先我们来创建一个枚举类型PromotionStrategy,用于作为选择优惠策略的参数:

package fun.swsk33site.strategy.promotion;/*** 优惠策略枚举*/
public enum PromotionStrategy {/*** 不使用优惠*/NO,/*** 满减优惠*/FULLDISCOUNT,/*** 返现优惠*/CASHBACK
}
复制代码

然后创建类PromotionContext,用于传入参数后选择相应的优惠策略,这个类就是我们策略模式的核心了:

package fun.swsk33site.strategy.promotion;import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.impl.CashBackPromotion;
import fun.swsk33site.strategy.promotion.impl.FullDiscountPromotion;
import fun.swsk33site.strategy.promotion.impl.NoPromotion;import java.util.HashMap;
import java.util.Map;/*** 优惠策略选择上下文,用于选择优惠策略*/
public class PromotionContext {// 用一个Map作为容器储存各个优惠策略的类,以枚举为参数取出private static Map<PromotionStrategy, Promotion> promotionMap = new HashMap<>();// 静态块用于初始化各个优惠策略实例static {promotionMap.put(PromotionStrategy.NO, new NoPromotion());promotionMap.put(PromotionStrategy.FULLDISCOUNT, new FullDiscountPromotion());promotionMap.put(PromotionStrategy.CASHBACK, new CashBackPromotion());}/*** 使用优惠** @param order    传入要使用优惠的订单* @param strategy 传入优惠策略*/public static void usePromotion(Order order, PromotionStrategy strategy) {Promotion getPromotion = promotionMap.get(strategy);getPromotion.doPromotion(order);}}
复制代码

可见,首先这个类中会把所有的优惠策略都实例化一遍并存入到一个Map中,每个优惠策略对应我们一个策略枚举值作为key,然后利用usePromotion方法,可以接受从客户端传来的优惠策略参数,然后选择相应的优惠策略。

可见,策略模式就是将选择策略的逻辑抽离到一个专门的类中,客户端就可以通过传参的形式更加灵活方便地选择策略。

我们来试一下子:

package fun.swsk33site.strategy;import fun.swsk33site.strategy.model.Order;
import fun.swsk33site.strategy.promotion.Promotion;
import fun.swsk33site.strategy.promotion.PromotionContext;
import fun.swsk33site.strategy.promotion.PromotionStrategy;
import fun.swsk33site.strategy.promotion.impl.CashBackPromotion;
import fun.swsk33site.strategy.promotion.impl.FullDiscountPromotion;
import fun.swsk33site.strategy.promotion.impl.NoPromotion;public class Client {/*** 客户端进行支付** @param order    待支付账单* @param strategy 要使用的优惠策略*/private static void doPayment(Order order, PromotionStrategy strategy) {System.out.println("账单:" + order.getName() + "准备支付");System.out.println("准备使用优惠");PromotionContext.usePromotion(order, strategy);System.out.println("支付完成:" + order.getPrice() + "元");}public static void main(String[] args) {// 模拟买东西Order order = new Order();order.setName("守望时空33购买的辛鹿咖啡豆:曼特宁拼配 深度烘焙1kg,意式极深炭烧 极深烘焙1kg,云南阿拉比卡庄园豆 深度烘焙454g");order.setPrice(202);// 进行支付,使用满减优惠策略doPayment(order, PromotionStrategy.FULLDISCOUNT);}}
复制代码

结果:

只需在进行支付的时候,调用策略选择类,利用枚举值传参,根据传参使用不同的优惠策略。

其实也可以不使用枚举传参,直接使用字符串或者常量等等方式都可以,不过枚举传参我认为可以降低错误的可能,实用一些。

3,总结

可见策略模式,在多策略的场景下是非常实用的。我们可以把策略模式的实现总结为以下几步:

  1. 抽象出策略接口,并实现不同的策略类
  2. 编写出策略选择类,其中根据不同的传入参数,选择或者执行对应的策略
  3. 客户端调用策略选择类,传入参数执行对应策略

策略模式增加了系统的可维护性,通常用在以下场景:

  • 一个系统需要在一类算法中动态地选择其中一种
  • 系统中有很多类,但是仅仅是行为不一样,需要根据情况使用其中的类

策略模式的优缺点也是很明显的:

  • 优点:

    • 符合开闭原则(尽量去扩展系统的功能而非去改写)
    • 避免了大量的if...else if...或者switch语句
  • 缺点:
    • 客户端需要知道所有的策略

最后附上整个示例的类图:

Java常用设计模式-策略模式相关推荐

  1. Java常用设计模式————原型模式(一)

    介绍 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 原型模式用于创建重复的对象,同时又能保证性能.当直接创建对象的代价比较大时,则采用 ...

  2. 常用设计模式-策略模式+工厂模式+模板模式(使用场景、解决方案)

    在策略模式+工厂模式中,没有使用到模板模式,因为张三和李四的业务逻辑都是调用AAA方法,如果现在在增加一个方法,次方法只需要李四一人去实现BBB方法,此时张三的handel中就会报错,需要张三也去实现 ...

  3. 常用设计模式-策略模式

    模式简介 它定义了算法家族,分别封装起来,让它们间可以相互替换,此模式让算法的变化,不会影响到使用算法的用户. 模式实现 环境类(Context) 用一个ConcreteStrategy对象来配置.维 ...

  4. Java常用设计模式————工厂模式

    简介: 工厂模式(Factory Pattern)是Java中最常用的设计模式之一,又称多态工厂模式.虚拟构造器模式.属于创建型模式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通 ...

  5. Java常用设计模式————组合模式

    引言 组合模式,是一种类似递归算法的结构性设计模式,通过以简单的 List ,组合本类对象,实现树状对象结构的"部分.整体"的层次. 它可以让调用程序不需要关心复杂对象与简单对象的 ...

  6. Java 常用设计模式 -- Builder模式

    Builder模式是在Java中最流行的模式之一.它很简单,有助于保持对象不可变,并且可以使用Project Lombok的@Builder或Immutables等工具生成,仅举几例. 模式的流畅变体 ...

  7. Java常用设计模式————桥接模式

    引言 在实际的业务中,经常会遇到多维度的概念组合,公园的门票,颐和园有年票.月票.日票,故宫也有年票.月票.日票.那么不同的公园和票种类型就可以视为两种不同的纬度,它们之间会形成相互组合的关系. 在类 ...

  8. Java常用设计模式————建造者模式

    引言 建造者模式(Builder Pattern)使用多个简单对象一步一步构建成一个复杂的对象.这种类型的设计模式属于建造型模式,它提供了一种创建对象的最佳方式. 一个Builder会一步步构建最终的 ...

  9. Java常用设计模式————外观模式

    引言 外观模式(Facade Pattern),又叫"过程模式".外观模式为子系统中的一组接口提供一个一致的入口,此模式定义了一个高层接口,这个接口使得这一组子系统更加易用. 一. ...

最新文章

  1. amazeui页面分析之登录页面
  2. wukong搜索引擎源码解读
  3. 如何在Eclipse中使用tomcat9 运行servlet开发简单的动态网页?
  4. maven配置阿里云_阿里云OSS PicGo 配置图床教程 超详细
  5. 【收藏】vue3+vite+ts 封装axios踩坑记录
  6. SAP Fiori里的Adapt UI按钮,神出鬼没的奥秘
  7. mysql为什么占用_mysql 3306端口被占用怎么办?
  8. UVALive7670 Asa's Chess Problem,上下界费用流,另类解法
  9. c++心形代码_c语言心形告白代码实现
  10. 自动填充html_第 11 篇:自动生成文章摘要
  11. IT-标准化-系列-15.在VPC 2007中构建虚拟网络
  12. (Electronic WorkBench)EWB仿真JK触发器
  13. 1、varargout与nargout区别
  14. java计算机毕业设计台球收费管理系统设计与实现MyBatis+系统+LW文档+源码+调试部署
  15. java如何把汉字转换成机内码_如何用java随机生成一个汉字?
  16. win10计算机 需要新应用,win10 计算器提示:需要新应用打开此calculator
  17. 王者荣耀刷金币C/C++语言脚本
  18. uni-app npm安装以及在uni-APP用npm安装echarts
  19. 官宣!2023ACP世界大赛晋级赛名单公布!
  20. 免费的ipad编辑php软件下载,免费也很香!8 款免费 APP 打造你的学术型 iPad

热门文章

  1. 如果生活中有什么结解不开,那就打个蝴蝶结吧
  2. 2021年9月22日-忆阻神经网络综述
  3. java8-ZoneId
  4. 生产者消费者模型的作用是什么
  5. 神经网络与深度学习(入门篇)
  6. CSS中的overflow,
  7. Jackson官网与官方文档
  8. 游戏礼包激活码案例分析
  9. python批量提取word指定内容_python批量提取word内信息
  10. 基于vue实现sku商品选择