- 代码地址
- 简介
- 代码示例
- 代码示例-2
- Spring Boot示例
- 总结
简介https://gitee.com/nssnail/design-pattern/tree/master/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F
策略模式(Strategy Pattern )又叫也叫政策模式( Policy Pattern ),它是将定义的算法家族、分别封装起来,让它们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户。属于行为型模式。
策略模式使用的就是面向对象的继承和多态机制,从而实现同一行为在不同场景下具备不同实现
策略模式可以解决在有多种算法相似的情况下,使用if…else或switch…case所带来的复杂性和臃肿性。在日常业务开发中,策略模式适用于以下场景∵
- 针对同一类型问题,有多种处理方式,每一种都能独立解决问题;
- 算法需要自由切换的场景;
- 需要屏蔽算法规则的场景。
类图如下
从UML类图中,我们可以看到,策略模式主要包含三种角色∶
- **上下文角色( Context )**∶用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;
- **抽象策略角色( Strategy ) **∶规定策略或算法的行为﹔
- 具体策略角色( ConcreteStrategy ):具体的策略或算法实现。
代码示例白话文解释: 比如在生活中我们有很多种优惠策略,返现优惠,折扣优惠,优惠券抵扣优惠等,我们用户只需要直到选择哪种优惠策略就好,并不需要关注算法的实现
IStrategy.java
//抽象策略类 Strategy
public interface IStrategy {
void algorithm();
}
ConcreteStrategyA.java
//具体策略类 ConcreteStrategy
public class ConcreteStrategyA implements IStrategy {
public void algorithm() {
System.out.println("Strategy A");
}
}
ConcreteStrategyB.java
//具体策略类 ConcreteStrategy
public class ConcreteStrategyB implements IStrategy {
public void algorithm() {
System.out.println("Strategy B");
}
}
Context.java
//上下文环境
public class Context {
private IStrategy mStrategy;
public Context(IStrategy strategy) {
this.mStrategy = strategy;
}
public void algorithm() {
this.mStrategy.algorithm();
}
}
Test.java
public class Test {
public static void main(String[] args) {
//选择一个具体策略
IStrategy strategy = new ConcreteStrategyA();
//来一个上下文环境
Context context = new Context(strategy);
//客户端直接让上下文环境执行算法
context.algorithm();
}
}
执行结果
Strategy A
但是这种方式是直接new对象后注入,并不能自行选择策略
代码示例-2接下来我们模拟一个优惠策略的选择
DiscountStrategy.java
public interface DiscountStrategy {
void doPromotion();
}
CashbackStrategy.java
public class CashbackStrategy implements DiscountStrategy {
public void doPromotion() {
System.out.println("返现策略");
}
}
CouponStrategy.java
public class CouponStrategy implements DiscountStrategy {
public void doPromotion() {
System.out.println("优惠券抵扣策略");
}
}
GroupbuyStrategy.java
public class GroupbuyStrategy implements DiscountStrategy {
public void doPromotion() {
System.out.println("拼团策略");
}
}
EmptyStrategy.java
public class EmptyStrategy implements DiscountStrategy {
public void doPromotion() {
System.out.println("无优惠");
}
}
这时候我们使用一个简单工厂来创建策略
DiscountStrategyFactory.java
public class DiscountStrategyFactory {
private DiscountStrategyFactory(){};
public static DiscountStrategy getStrategy(String name){
if(name.equals("cashBack")){
return new CashbackStrategy();
}else if(name.equals("coupon")){
return new CouponStrategy();
}else if(name.equals("groupbuy")){
return new GroupbuyStrategy();
}else {
return new EmptyStrategy();
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
DiscountStrategy cashBack = DiscountStrategyFactory.create("cashBack");
cashBack.doPromotion();
}
}
执行结果:
返现策略
但是我们这样还是没有消除if…esle判断,接下来我们再优化下
添加DiscountConstant.java
public class DiscountConstant {
public static final String CASH_BACK="cashBack";
public static final String COUPON="coupon";
public static final String GROUP_BY="groupBy";
public static final String EMPTY="empty";
}
修改工厂类DiscountStrategyFactory.java
public class DiscountStrategyFactory {
//策略容器
private static Map PROMOTIONS=new HashMap();
//设置单例
private DiscountStrategyFactory(){};
//初始化策略列表
static {
PROMOTIONS.put(DiscountConstant.CASH_BACK,new CashbackStrategy());
PROMOTIONS.put(DiscountConstant.COUPON,new CouponStrategy());
PROMOTIONS.put(DiscountConstant.GROUP_BY,new GroupbuyStrategy());
PROMOTIONS.put(DiscountConstant.EMPTY,new EmptyStrategy());
}
//获取策略
public static DiscountStrategy getStrategy(String name){
DiscountStrategy strategy = PROMOTIONS.get(name);
return strategy == null ? PROMOTIONS.get(DiscountConstant.EMPTY) : strategy;
}
//获取策略名称列表
public static Set getPromotionKeys(){
return PROMOTIONS.keySet();
}
}
Test.java
public class Test {
public static void main(String[] args) {
//用户获取优惠策略列表
Set promotionKeys = DiscountStrategyFactory.getPromotionKeys();
System.out.println(promotionKeys);
//用户根据策略列表选择策略
DiscountStrategy cashBack = DiscountStrategyFactory.getStrategy("coupon");
cashBack.doPromotion();
}
}
这样我们就消除了if…else的判断,通过map的性质来获取策略
注:这里还能使用配置文件然后通过反射来获取策略,一样可以消除if...else,如一些缓存淘汰策略
Spring Boot示例在spring中,我们可以将策略放入容器中
添加pom.xml依赖
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.7.RELEASE com.sise 策略模式 1.0-SNAPSHOT 1.8 UTF-8 2.3.0.RELEASE org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-web
添加配置文件application.properties
server.port=9096 spring.application.name=demo
DiscountStrategy.java
public interface DiscountStrategy {
String doPromotion();
}
CashbackStrategy.java
@Component(DiscountConstant.CASH_BACK)
public class CashbackStrategy implements DiscountStrategy {
public String doPromotion() {
return "返现策略";
}
}
CouponStrategy.java
@Component(DiscountConstant.COUPON)
public class CouponStrategy implements DiscountStrategy {
public String doPromotion() {
return "优惠券抵扣策略";
}
}
GroupbuyStrategy.java
@Component(DiscountConstant.GROUP_BY)
public class GroupbuyStrategy implements DiscountStrategy {
public String doPromotion() {
return "拼团策略";
}
}
EmptyStrategy.java
@Component(DiscountConstant.EMPTY)
public class EmptyStrategy implements DiscountStrategy {
public String doPromotion() {
return "无优惠";
}
}
TestController.java 测试接口类
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private ApplicationContext applicationContext;
@RequestMapping("/doPromotion/{strategy}")
public String doPromotion(@PathVariable String strategy){
DiscountStrategy bean = applicationContext.getBean(strategy, DiscountStrategy.class);
if(ObjectUtils.isEmpty(bean)){
bean=new EmptyStrategy();
}
return bean.doPromotion();
}
}
接口测试 http://localhost:9096/test/doPromotion/cashBack
总结更换参数 http://localhost:9096/test/doPromotion/coupon
1. 优点
(1)策略模式符合开闭原则。
(2)避免使用多重条件转移语句,如i…else…语句、switch语句
(3)使用策略模式可以提高算法的保密性和安全性。
2. 缺点
(1)客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
(2)代码中会产生非常多策略类,增加维护难度。



